diff --git a/composer.json b/composer.json index 03ab193a2..69b126f93 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "symfony/twig-bundle": "6.4.*" }, "require-dev": { - "phpunit/phpunit": "9.6.*", + "phpunit/phpunit": "10.5.*", "symfony/dom-crawler": "6.4.*", "symfony/config": "6.4.*", "symfony/runtime": "6.4.*", diff --git a/phpunit.xml b/phpunit.xml index 6430a937b..160a830f2 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,14 +1,9 @@ - - - - src/controller - src/model - src/model/sparql - - - vendor - + + + + + @@ -17,7 +12,17 @@ - tests + tests + + + src/controller + src/model + src/model/sparql + + + vendor + + diff --git a/src/model/resolver/LOCResource.php b/src/model/resolver/LOCResource.php index e51259b53..9658e1d76 100644 --- a/src/model/resolver/LOCResource.php +++ b/src/model/resolver/LOCResource.php @@ -16,6 +16,9 @@ public function resolve(int $timeout): ?EasyRdf\Resource 'timeout' => $timeout)); $context = stream_context_create($opts); $fd = fopen($this->uri, 'rb', false, $context); + if ($fd === false) { + return null; + } $headers = stream_get_meta_data($fd)['wrapper_data']; foreach ($headers as $header) { if (strpos(strtolower($header), 'x-preflabel:') === 0) { diff --git a/tests/ConceptPropertyValueLiteralTest.php b/tests/ConceptPropertyValueLiteralTest.php index 55e0d27a6..708de61a3 100644 --- a/tests/ConceptPropertyValueLiteralTest.php +++ b/tests/ConceptPropertyValueLiteralTest.php @@ -61,22 +61,22 @@ public function testGetLabelThatIsADate() */ public function testGetLabelThatIsABrokenDate() { - set_error_handler(function ($code, $message) { - throw new \PHPUnit\Framework\Error($message,$code); - }); + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); - try { - $vocab = $this->model->getVocabulary('dates'); + $vocab = $this->model->getVocabulary('dates'); - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Failed to parse time string (1986-21-00) at position 6 (1): Unexpected character"); + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Failed to parse time string (1986-21-00) at position 6 (1): Unexpected character"); - $concept = $vocab->getConceptInfo("http://www.skosmos.skos/date/d2", "en"); - $props = $concept->getProperties(); - $propvals = $props['http://www.skosmos.skos/date/ownDate']->getValues(); - } finally { - restore_error_handler(); - } + $concept = $vocab->getConceptInfo("http://www.skosmos.skos/date/d2", "en"); + $props = $concept->getProperties(); + $propvals = $props['http://www.skosmos.skos/date/ownDate']->getValues(); } /** diff --git a/tests/ConceptTest.php b/tests/ConceptTest.php index b8686d61d..88d4d5a30 100644 --- a/tests/ConceptTest.php +++ b/tests/ConceptTest.php @@ -227,7 +227,8 @@ public function testGetMappingPropertiesWithIdenticalLabels() { $vocab = $this->model->getVocabulary('duplicates'); $concept = $vocab->getConceptInfo("http://www.skosmos.skos/dup/d3", "en"); - $props = $concept->getMappingProperties(); + // suppress fsockopen PHP warnings caused by trying to resolve www.skosmos.skos + @$props = $concept->getMappingProperties(); $values = $props['skos:closeMatch']->getValues(); $this->assertCount(2, $values); } @@ -312,22 +313,21 @@ public function testGetDateWithCreatedAndModified() */ public function testGetTimestampInvalidWarning() { - set_error_handler(function ($code, $message) { - throw new \PHPUnit\Framework\Error($message, $code); - }); + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); - try { - $vocab = $this->model->getVocabulary('test'); - - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Failed to parse time string (1986-21-00) at position 6 (1): Unexpected character"); + $vocab = $this->model->getVocabulary('test'); - $concept = $vocab->getConceptInfo("http://www.skosmos.skos/test/ta114", "en"); - $concept->getDate(); # this should throw an ErrorException + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Failed to parse time string (1986-21-00) at position 6 (1): Unexpected character"); - } finally { - restore_error_handler(); - } + $concept = $vocab->getConceptInfo("http://www.skosmos.skos/test/ta114", "en"); + $concept->getDate(); # this should throw an ErrorException } /** @@ -612,7 +612,7 @@ public function testProcessExternalResource() * Data provider for testGetModifiedDate test method. * @return array */ - public function modifiedDateDataProvider() + public static function modifiedDateDataProvider() { return [ ["cat", "2018-12-13T06:28:14", "+00:00"], # set #0 diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index aa467d358..37661e015 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -19,7 +19,8 @@ protected function setUp(): void public function testResolveLOCZeroTimeout() { $uri = "http://id.loc.gov/authorities/subjects/sh85016673"; // LCSH: Breakwaters - $resource = $this->resolver->resolve($uri, 0); + // use @ to suppress the timeout warning + @$resource = $this->resolver->resolve($uri, 0); $this->assertNull($resource); } @@ -32,7 +33,8 @@ public function testResolveLOCZeroTimeout() public function testResolveWDQSZeroTimeout() { $uri = "http://www.wikidata.org/entity/Q42"; // Wikidata: Douglas Adams - $resource = $this->resolver->resolve($uri, 0); + // use @ to suppress the timeout warning + @$resource = $this->resolver->resolve($uri, 0); $this->assertNull($resource); } @@ -45,7 +47,8 @@ public function testResolveWDQSZeroTimeout() public function testResolveLDZeroTimeout() { $uri = "http://paikkatiedot.fi/so/1000772/10048472"; // PNR: Ahlainen - $resource = $this->resolver->resolve($uri, 0); + // use @ to suppress the timeout warning + @$resource = $this->resolver->resolve($uri, 0); $this->assertNull($resource); } diff --git a/tests/RestControllerTest.php b/tests/RestControllerTest.php index 5d5052759..4d876e920 100644 --- a/tests/RestControllerTest.php +++ b/tests/RestControllerTest.php @@ -26,13 +26,15 @@ protected function tearDown(): void */ public function testDataAsJson() { + ob_start(); + $request = new Request($this->model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta117'); $request->setVocab("test"); $this->controller->data($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setQueryParam('query', '*bass'); $this->controller->search($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setQueryParam('query', '*bass'); $request->setQueryParam('fields', 'broader relatedMatch'); $this->controller->search($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setQueryParam('query', $query); $request->setQueryParam('fields', 'broader relatedMatch'); $this->controller->search($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); + if ($isSuccess) { $this->assertStringNotContainsString("400 Bad Request", $out, "The REST search call returned an unexpected 400 bad request error!"); } else { @@ -344,12 +353,14 @@ public function testSearchWithZero(string $query, bool $isSuccess) */ public function testIndexLettersJsonLd() { + ob_start(); + $request = new Request($this->model); $request->setVocab('test'); $request->setLang('en'); $this->controller->indexLetters($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setVocab('test'); $request->setLang('en'); $this->controller->indexConcepts("B", $request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setVocab('test'); $request->setLang('en'); $request->setQueryParam('limit', '2'); $this->controller->indexConcepts("B", $request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setVocab('test'); $request->setLang('en'); $request->setQueryParam('offset', '1'); $this->controller->indexConcepts("B", $request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setVocab('test'); $request->setLang('en'); @@ -532,7 +551,7 @@ public function testIndexConceptsJsonLdLimitOffset() $request->setQueryParam('offset', '1'); $this->controller->indexConcepts("B", $request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta112'); @@ -572,7 +593,7 @@ public function testLabelOnePrefOneAltLabel() $request->setLang('en'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta112'); $request->setLang('en'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/nonexistent/vocab'); $request->setLang('en'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = "404 Not Found : Could not find concept "; $this->assertEquals($expected, $out); @@ -649,6 +674,8 @@ public function testLabelGlobalNonexistentVocab() */ public function testLabelOnePrefOneHiddenLabel() { + ob_start(); + $request = new Request($this->model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta112'); @@ -656,7 +683,7 @@ public function testLabelOnePrefOneHiddenLabel() $request->setLang('fi'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta111'); @@ -690,7 +719,7 @@ public function testLabelOnePrefLabel() $request->setLang('en'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/ta111'); @@ -721,7 +752,7 @@ public function testLabelNoPrefLabel() $request->setLang('sv'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test/nonexistent'); @@ -751,7 +784,7 @@ public function testLabelNoConcept() $request->setLang('en'); $this->controller->label($request); - $out = $this->getActualOutput(); + $out = ob_get_clean(); $expected = "404 Not Found : Could not find concept "; $this->assertEquals($expected, $out); @@ -762,6 +795,8 @@ public function testLabelNoConcept() */ public function testNewConcepts() { + ob_start(); + $request = new Request($this->model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/changes'); @@ -771,7 +806,7 @@ public function testNewConcepts() $request->setQueryParam('offset', '0'); $this->controller->newConcepts($request); - $changeList = $this->getActualOutput(); + $changeList = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/test'); @@ -832,7 +869,7 @@ public function testModifiedConcepts() $request->setQueryParam('offset', '0'); $this->controller->modifiedConcepts($request); - $changeList = $this->getActualOutput(); + $changeList = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setURI('http://www.skosmos.skos/changes'); @@ -866,7 +905,7 @@ public function testDeprecatedChanges() $request->setQueryParam('offset', '0'); $this->controller->modifiedConcepts($request); - $changeList = $this->getActualOutput(); + $changeList = ob_get_clean(); $expected = <<model); $request->setQueryParam('format', 'application/json'); $request->setVocab('test'); $request->setLang('en'); $this->controller->vocabularyStatistics($request); - $statistics = $this->getActualOutput(); + $statistics = ob_get_clean(); + $expected = <<model->getVocabulary('testdiff'); - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Default language for vocabulary 'testdiff' unknown, choosing 'en'."); - $lang = $vocab->getConfig()->getDefaultLanguage(); - } finally { - restore_error_handler(); - } + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); + + $vocab = $this->model->getVocabulary('testdiff'); + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Default language for vocabulary 'testdiff' unknown, choosing 'en'."); + $lang = $vocab->getConfig()->getDefaultLanguage(); } /** @@ -217,19 +218,19 @@ public function testGetDataURLs() */ public function testGetDataURLsNotGuessable() { - set_error_handler(function ($code, $message) { - throw new \PHPUnit\Framework\Error($message, $code); - }); + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); - try { - $vocab = $this->model->getVocabulary('test'); - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Could not guess format for ."); + $vocab = $this->model->getVocabulary('test'); + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Could not guess format for ."); - $url = $vocab->getConfig()->getDataURLs(); - } finally { - restore_error_handler(); - } + $url = $vocab->getConfig()->getDataURLs(); } /** @@ -259,21 +260,21 @@ public function testGetDataURLsMarc() */ public function testGetDataURLsMarcNotDefined() { - set_error_handler(function ($code, $message) { - throw new \PHPUnit\Framework\Error($message, $code); - }); + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); - try { - $vocab = $this->model->getVocabulary('marc-undefined'); + $vocab = $this->model->getVocabulary('marc-undefined'); - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Could not guess format for ."); + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Could not guess format for ."); - $url = $vocab->getConfig()->getDataURLs(); - $this->assertEquals(array(), $url); - } finally { - restore_error_handler(); - } + $url = $vocab->getConfig()->getDataURLs(); + $this->assertEquals(array(), $url); } /** @@ -686,20 +687,20 @@ public function testGetPropertyOrderDefault() */ public function testGetPropertyOrderUnknown() { - set_error_handler(function ($code, $message) { - throw new \PHPUnit\Framework\Error($message, $code); - }); + set_error_handler( + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new UserWarning( $errstr, $errno ); + }, + E_ALL + ); - try { - $vocab = $this->model->getVocabulary('testUnknownPropertyOrder'); - $this->expectException(\PHPUnit\Framework\Error::class); - $this->expectExceptionMessage("Property order for vocabulary 'testUnknownPropertyOrder' unknown, using default order"); + $vocab = $this->model->getVocabulary('testUnknownPropertyOrder'); + $this->expectException(UserWarning::class); + $this->expectExceptionMessage("Property order for vocabulary 'testUnknownPropertyOrder' unknown, using default order"); - $params = $vocab->getConfig()->getPropertyOrder(); - $this->assertEquals(VocabularyConfig::DEFAULT_PROPERTY_ORDER, $params); - } finally { - restore_error_handler(); - } + $params = $vocab->getConfig()->getPropertyOrder(); + $this->assertEquals(VocabularyConfig::DEFAULT_PROPERTY_ORDER, $params); } /** diff --git a/tests/WebControllerTest.php b/tests/WebControllerTest.php index 7cc588a83..4aa8e9dbd 100644 --- a/tests/WebControllerTest.php +++ b/tests/WebControllerTest.php @@ -20,67 +20,67 @@ protected function setUp(): void * methods that perform the action are abstracted away, this data provider works for both. * @return array */ - public function gitAndConfigModifiedDateDataProvider() + public static function gitAndConfigModifiedDateDataProvider() { return [ # cache disabled [ false, # cache enabled null, # cached value - $this->datetime('01-Feb-2009') # modified date time + self::datetime('01-Feb-2009') # modified date time ], # set #0 # cache enabled, but nothing fetched [ true, # cache enabled null, # cached value - $this->datetime('01-Feb-2009') # modified date time + self::datetime('01-Feb-2009') # modified date time ], # set #1 # cache enabled, cached value returned [ true, # cache enabled - $this->datetime('01-Feb-2009'), # cached value + self::datetime('01-Feb-2009'), # cached value null # modified date time ], # set #2 ]; } - public function modifiedDateDataProvider() + public static function modifiedDateDataProvider() { return [ # concept has the most recent date time [ - $this->datetime('01-Feb-2011'), # concept - $this->datetime('01-Feb-2002'), # git - $this->datetime('01-Feb-2003'), # config - $this->datetime('01-Feb-2011') # returned + self::datetime('01-Feb-2011'), # concept + self::datetime('01-Feb-2002'), # git + self::datetime('01-Feb-2003'), # config + self::datetime('01-Feb-2011') # returned ], # set #0 # concept has the most recent date time [ - $this->datetime('01-Feb-2011'), # concept + self::datetime('01-Feb-2011'), # concept null, # git - $this->datetime('01-Feb-2003'), # config - $this->datetime('01-Feb-2011') # returned + self::datetime('01-Feb-2003'), # config + self::datetime('01-Feb-2011') # returned ], # set #1 # concept has the most recent date time [ - $this->datetime('01-Feb-2011'), # concept + self::datetime('01-Feb-2011'), # concept null, # git null, # config - $this->datetime('01-Feb-2011') # returned + self::datetime('01-Feb-2011') # returned ], # set #2 # git has the most recent date time [ - $this->datetime('01-Feb-2001'), # concept - $this->datetime('01-Feb-2012'), # git - $this->datetime('01-Feb-2003'), # config - $this->datetime('01-Feb-2012') # returned + self::datetime('01-Feb-2001'), # concept + self::datetime('01-Feb-2012'), # git + self::datetime('01-Feb-2003'), # config + self::datetime('01-Feb-2012') # returned ], # set #3 # config has the most recent date time [ - $this->datetime('01-Feb-2001'), # concept - $this->datetime('01-Feb-2002'), # git - $this->datetime('01-Feb-2013'), # config - $this->datetime('01-Feb-2013') # returned + self::datetime('01-Feb-2001'), # concept + self::datetime('01-Feb-2002'), # git + self::datetime('01-Feb-2013'), # config + self::datetime('01-Feb-2013') # returned ], # set #4 # no date time found [ @@ -97,7 +97,7 @@ public function modifiedDateDataProvider() * @param string $string * @return bool|DateTime */ - private function datetime(string $string) + private static function datetime(string $string) { return DateTime::createFromFormat("j-M-Y", $string); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5fa2cde45..9e3d9cbae 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,12 +9,9 @@ putenv('SKOSMOS_SPARQL_ENDPOINT=http://localhost:9030/skosmos/sparql'); } -# Allow running git commands in the php-actions/phpunit container -# (prevent "dubious ownership" error; /app is owned by another user, not root) -if (getenv('GITHUB_ACTIONS') === 'true') { - mkdir('/home/runner'); - exec('git config --global --add safe.directory /app'); -} - require_once(__DIR__ . '/../vendor/autoload.php'); require_once(__DIR__ . '/../src/model/Model.php'); + +class UserWarning extends \Exception { + // custom exception class to capture E_USER_WARNING in PHPUnit tests +}