From 1fdc97e6653b5d6976958b9d07a462c7f3436928 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 14:11:51 +0200 Subject: [PATCH 1/9] allow running tests via composer script --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index a06511d9..49df4782 100644 --- a/composer.json +++ b/composer.json @@ -38,5 +38,8 @@ "psr-4": { "Redmine\\Tests\\": "tests/" } + }, + "scripts": { + "test": "phpunit" } } From e14a06c450129746650421b9d2eb764c42bf7bc8 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 14:34:06 +0200 Subject: [PATCH 2/9] Update all dev-dependencies --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 49df4782..5b2a26ff 100644 --- a/composer.json +++ b/composer.json @@ -24,10 +24,10 @@ "psr/http-factory": "^1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "3.11.0", - "phpunit/phpunit": "9.5.10", - "guzzlehttp/psr7": "2.1.0", - "php-mock/php-mock-phpunit": "2.6.0" + "friendsofphp/php-cs-fixer": "^3", + "phpunit/phpunit": "^9", + "guzzlehttp/psr7": "^2", + "php-mock/php-mock-phpunit": "^2.6" }, "autoload": { "psr-4": { From 81f5f774887a5087a780caa7adf02e64ffb72417 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 14:37:24 +0200 Subject: [PATCH 3/9] fix covers warnings --- tests/Unit/Api/IssueTest.php | 5 ----- tests/Unit/Api/MembershipTest.php | 1 - 2 files changed, 6 deletions(-) diff --git a/tests/Unit/Api/IssueTest.php b/tests/Unit/Api/IssueTest.php index aa94b1c3..81fe5dc2 100644 --- a/tests/Unit/Api/IssueTest.php +++ b/tests/Unit/Api/IssueTest.php @@ -475,7 +475,6 @@ public function testCreateCleansParameters() * Test create() and buildXML(). * * @covers ::create - * @covers ::buildXML * @covers ::attachCustomFieldXML * @test */ @@ -719,7 +718,6 @@ public function testAddNoteToIssue() /** * Test buildXML(). * - * @covers ::buildXML * @test */ public function testBuildXmlWithCustomFields() @@ -760,7 +758,6 @@ public function testBuildXmlWithCustomFields() /** * Test buildXML(). * - * @covers ::buildXML * @test */ public function testBuildXmlWithWatchers() @@ -796,7 +793,6 @@ public function testBuildXmlWithWatchers() /** * Test buildXML(). * - * @covers ::buildXML * @test */ public function testBuildXmlWithUploads() @@ -857,7 +853,6 @@ public function testBuildXmlWithUploads() /** * Test buildXML(). * - * @covers ::buildXML * @test */ public function testBuildXmlWithWatcherAndUploadAndCustomFieldAndStandard() diff --git a/tests/Unit/Api/MembershipTest.php b/tests/Unit/Api/MembershipTest.php index 40c46478..b64b0c0d 100644 --- a/tests/Unit/Api/MembershipTest.php +++ b/tests/Unit/Api/MembershipTest.php @@ -220,7 +220,6 @@ public function testCreateCallsPost() * Test create(). * * @covers ::create - * @covers ::buildXML * @test */ public function testCreateBuildsXml() From 3f916118062de23066c5fee8773eb5398887917c Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 14:53:31 +0200 Subject: [PATCH 4/9] set all dataProvider as static --- .../Psr18ClientRequestGenerationTest.php | 2 +- tests/Unit/Api/AbstractApiTest.php | 8 ++++---- tests/Unit/Api/AttachmentTest.php | 2 +- tests/Unit/Api/IssueTest.php | 2 +- tests/Unit/Api/UserTest.php | 2 +- tests/Unit/Api/VersionTest.php | 6 +++--- tests/Unit/Client/NativeCurlClientTest.php | 4 ++-- tests/Unit/Client/Psr18ClientTest.php | 4 ++-- tests/Unit/Serializer/JsonSerializerTest.php | 16 +++++++++------- tests/Unit/Serializer/PathSerializerTest.php | 2 +- tests/Unit/Serializer/XmlSerializerTest.php | 8 ++++---- 11 files changed, 29 insertions(+), 27 deletions(-) diff --git a/tests/Integration/Psr18ClientRequestGenerationTest.php b/tests/Integration/Psr18ClientRequestGenerationTest.php index 7aad16cf..9c77eb42 100644 --- a/tests/Integration/Psr18ClientRequestGenerationTest.php +++ b/tests/Integration/Psr18ClientRequestGenerationTest.php @@ -107,7 +107,7 @@ public function createStreamFromResource($resource): StreamInterface $client->$method($path, $data); } - public function createdGetRequestsData() + public static function createdGetRequestsData(): array { return [ [ diff --git a/tests/Unit/Api/AbstractApiTest.php b/tests/Unit/Api/AbstractApiTest.php index 5bf704c9..d959e1e3 100644 --- a/tests/Unit/Api/AbstractApiTest.php +++ b/tests/Unit/Api/AbstractApiTest.php @@ -30,7 +30,7 @@ public function testIsNotNullReturnsCorrectBoolean(bool $expected, $value) $this->assertSame($expected, $method->invoke($api, $value)); } - public function getIsNotNullReturnsCorrectBooleanData() + public static function getIsNotNullReturnsCorrectBooleanData(): array { return [ [false, null], @@ -67,7 +67,7 @@ public function testLastCallFailedReturnsCorrectBoolean($statusCode, $expectedBo $this->assertSame($expectedBoolean, $api->lastCallFailed()); } - public function getLastCallFailedData() + public static function getLastCallFailedData(): array { return [ [100, true], @@ -161,7 +161,7 @@ public function testJsonDecodingFromGetMethod($response, $decode, $expected) } } - public function getJsonDecodingFromGetMethodData() + public static function getJsonDecodingFromGetMethodData(): array { return [ ['{"foo_bar": 12345}', null, ['foo_bar' => 12345]], // test decode by default @@ -217,7 +217,7 @@ public function testXmlDecodingFromRequestMethods($methodName, $response, $decod } } - public function getXmlDecodingFromGetMethodData() + public static function getXmlDecodingFromGetMethodData(): array { return [ ['get', '', null, ''], // test decode by default diff --git a/tests/Unit/Api/AttachmentTest.php b/tests/Unit/Api/AttachmentTest.php index cbb99750..5bd3df46 100644 --- a/tests/Unit/Api/AttachmentTest.php +++ b/tests/Unit/Api/AttachmentTest.php @@ -44,7 +44,7 @@ public function testLastCallFailedTrue($responseCode, $hasFailed) * * @return array[] */ - public function responseCodeProvider() + public static function responseCodeProvider(): array { return [ [199, true], diff --git a/tests/Unit/Api/IssueTest.php b/tests/Unit/Api/IssueTest.php index 81fe5dc2..362d816e 100644 --- a/tests/Unit/Api/IssueTest.php +++ b/tests/Unit/Api/IssueTest.php @@ -13,7 +13,7 @@ */ class IssueTest extends TestCase { - public function getPriorityConstantsData() + public static function getPriorityConstantsData(): array { return [ [1, Issue::PRIO_LOW], diff --git a/tests/Unit/Api/UserTest.php b/tests/Unit/Api/UserTest.php index 6c3f526f..e3746fcc 100644 --- a/tests/Unit/Api/UserTest.php +++ b/tests/Unit/Api/UserTest.php @@ -323,7 +323,7 @@ public function testCreateThrowsExceptionIfValueIsMissingInParameters($parameter * * @return array[] */ - public function incompleteCreateParameterProvider() + public static function incompleteCreateParameterProvider(): array { return [ // Missing Login diff --git a/tests/Unit/Api/VersionTest.php b/tests/Unit/Api/VersionTest.php index 77e4c88e..668d8d38 100644 --- a/tests/Unit/Api/VersionTest.php +++ b/tests/Unit/Api/VersionTest.php @@ -909,7 +909,7 @@ public function testUpdateThrowsExceptionWithInvalidSharing($sharingValue) * * @return array[] */ - public function validSharingProvider() + public static function validSharingProvider(): array { return [ ['none', 'none'], @@ -925,7 +925,7 @@ public function validSharingProvider() * * @return array[] */ - public function validEmptySharingProvider() + public static function validEmptySharingProvider(): array { return [ [null], @@ -939,7 +939,7 @@ public function validEmptySharingProvider() * * @return array[] */ - public function invalidSharingProvider() + public static function invalidSharingProvider(): array { return [ ['all'], diff --git a/tests/Unit/Client/NativeCurlClientTest.php b/tests/Unit/Client/NativeCurlClientTest.php index bfceb52c..2adaad00 100644 --- a/tests/Unit/Client/NativeCurlClientTest.php +++ b/tests/Unit/Client/NativeCurlClientTest.php @@ -744,7 +744,7 @@ public function testRequestsReturnsCorrectContent($method, $data, $boolReturn, $ $this->assertSame($content, $client->getLastResponseBody()); } - public function getRequestReponseData() + public static function getRequestReponseData(): array { return [ ['requestGet', '', true, 101, 'text/plain', ''], @@ -865,7 +865,7 @@ public function getApiShouldReturnApiInstance($apiName, $class) $this->assertInstanceOf($class, $client->getApi($apiName)); } - public function getApiClassesProvider() + public static function getApiClassesProvider(): array { return [ ['attachment', 'Redmine\Api\Attachment'], diff --git a/tests/Unit/Client/Psr18ClientTest.php b/tests/Unit/Client/Psr18ClientTest.php index 4b04f526..4b028163 100644 --- a/tests/Unit/Client/Psr18ClientTest.php +++ b/tests/Unit/Client/Psr18ClientTest.php @@ -206,7 +206,7 @@ public function testRequestsReturnsCorrectContent($method, $data, $boolReturn, $ $this->assertSame($content, $client->getLastResponseBody()); } - public function getRequestReponseData() + public static function getRequestReponseData(): array { return [ ['requestGet', '', true, 101, 'text/plain', ''], @@ -253,7 +253,7 @@ public function getApiShouldReturnApiInstance($apiName, $class) $this->assertInstanceOf($class, $client->getApi($apiName)); } - public function getApiClassesProvider() + public static function getApiClassesProvider(): array { return [ ['attachment', 'Redmine\Api\Attachment'], diff --git a/tests/Unit/Serializer/JsonSerializerTest.php b/tests/Unit/Serializer/JsonSerializerTest.php index 54b25d3a..29e65fad 100644 --- a/tests/Unit/Serializer/JsonSerializerTest.php +++ b/tests/Unit/Serializer/JsonSerializerTest.php @@ -10,7 +10,7 @@ class JsonSerializerTest extends TestCase { - public function getEncodedToNormalizedData() + public static function getEncodedToNormalizedData(): array { return [ [ @@ -68,7 +68,7 @@ public function createFromStringDecodesToExpectedNormalizedData(string $data, $e $this->assertSame($expected, $serializer->getNormalized()); } - public function getInvalidEncodedData() + public static function getInvalidEncodedData(): array { return [ [ @@ -95,7 +95,7 @@ public function createFromStringWithInvalidStringThrowsException(string $message $serializer = JsonSerializer::createFromString($data); } - public function getNormalizedToEncodedData() + public static function getNormalizedToEncodedData(): array { return [ [ @@ -197,11 +197,13 @@ public function createFromArrayDecodesToExpectedString(array $data, $expected) $this->assertSame($expected, $encoded); } - public function getInvalidSerializedData() + public static function getInvalidSerializedData(): array { - yield [ - 'Could not encode JSON from array: Type is not supported', - [fopen('php://temp', 'r+')], + return [ + [ + 'Could not encode JSON from array: Type is not supported', + [fopen('php://temp', 'r+')], + ], ]; } diff --git a/tests/Unit/Serializer/PathSerializerTest.php b/tests/Unit/Serializer/PathSerializerTest.php index d73fd063..444403dc 100644 --- a/tests/Unit/Serializer/PathSerializerTest.php +++ b/tests/Unit/Serializer/PathSerializerTest.php @@ -9,7 +9,7 @@ class PathSerializerTest extends TestCase { - public function getPathData() + public static function getPathData(): array { return [ [ diff --git a/tests/Unit/Serializer/XmlSerializerTest.php b/tests/Unit/Serializer/XmlSerializerTest.php index e26fbd54..201ced73 100644 --- a/tests/Unit/Serializer/XmlSerializerTest.php +++ b/tests/Unit/Serializer/XmlSerializerTest.php @@ -10,7 +10,7 @@ class XmlSerializerTest extends TestCase { - public function getEncodedToNormalizedData() + public static function getEncodedToNormalizedData(): array { return [ [ @@ -71,7 +71,7 @@ public function createFromStringDecodesToExpectedNormalizedData(string $data, $e $this->assertSame($expected, $serializer->getNormalized()); } - public function getInvalidEncodedData() + public static function getInvalidEncodedData(): array { return [ [ @@ -110,7 +110,7 @@ public function createFromStringWithInvalidStringThrowsException(string $message $serializer = XmlSerializer::createFromString($data); } - public function getNormalizedToEncodedData() + public static function getNormalizedToEncodedData(): array { return [ [ @@ -201,7 +201,7 @@ public function createFromArrayDecodesToExpectedString(array $data, $expected) $this->assertSame($expected, trim($dom->saveXML())); } - public function getInvalidSerializedData() + public static function getInvalidSerializedData(): iterable { if (version_compare(\PHP_VERSION, '8.0.0', '<')) { // old Exception message for PHP 7.4 From 5d02285744c07862242e9a3a4e9510915f5e2d4f Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 15:00:45 +0200 Subject: [PATCH 5/9] Improve github Actions, update services, add tests with PHP 8.2 --- .github/workflows/tests.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 930b2bdd..1e6dcd02 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,11 +14,11 @@ jobs: fail-fast: false matrix: operating-system: ['ubuntu-latest'] - php-versions: ['7.4', '8.0', '8.1'] + php-versions: ['7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 @@ -32,21 +32,10 @@ jobs: extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite coverage: xdebug - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - # Use composer.json for key, if composer.lock is not committed. - # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install Composer dependencies - run: composer install --no-progress --prefer-dist --optimize-autoloader + # Install composer dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: "Install Composer dependencies" + uses: "ramsey/composer-install@v2" - name: Run tests run: vendor/bin/phpunit --coverage-text From 5eb6017fe65e150878fd4592b6d475897c4e17f1 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 16:05:00 +0200 Subject: [PATCH 6/9] Run tests with phpunit 10.2, replace deprecated withConsecutive() --- .gitignore | 3 +- composer.json | 2 +- phpunit.xml.dist | 16 +-- src/Redmine/Serializer/XmlSerializer.php | 1 + tests/Unit/Client/NativeCurlClientTest.php | 149 ++++++-------------- tests/Unit/Client/Psr18ClientTest.php | 13 +- tests/Unit/Serializer/XmlSerializerTest.php | 23 +-- 7 files changed, 63 insertions(+), 144 deletions(-) diff --git a/.gitignore b/.gitignore index f4581bc8..03d27816 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .php-cs-fixer.cache -.phpunit.result.cache +.phpunit.cache composer.lock composer.phar -coverage.clover phpunit.xml vendor diff --git a/composer.json b/composer.json index 5b2a26ff..49755dd9 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3", - "phpunit/phpunit": "^9", + "phpunit/phpunit": "^9 || 10.2.*", "guzzlehttp/psr7": "^2", "php-mock/php-mock-phpunit": "^2.6" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f9c81635..edf5b133 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,6 @@ - - - - src/Redmine/ - - - - - + + tests/Unit/ @@ -20,4 +13,9 @@ + + + src/Redmine/ + + diff --git a/src/Redmine/Serializer/XmlSerializer.php b/src/Redmine/Serializer/XmlSerializer.php index 682c6392..691d5fb2 100644 --- a/src/Redmine/Serializer/XmlSerializer.php +++ b/src/Redmine/Serializer/XmlSerializer.php @@ -2,6 +2,7 @@ namespace Redmine\Serializer; +use JsonException; use Redmine\Exception\SerializerException; use SimpleXMLElement; use Throwable; diff --git a/tests/Unit/Client/NativeCurlClientTest.php b/tests/Unit/Client/NativeCurlClientTest.php index 2adaad00..caf0ddf1 100644 --- a/tests/Unit/Client/NativeCurlClientTest.php +++ b/tests/Unit/Client/NativeCurlClientTest.php @@ -140,20 +140,11 @@ public function testStartAndStopImpersonateUser() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -208,20 +199,11 @@ public function testSetSslVersion() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -277,20 +259,11 @@ public function testSetSslVerifypeer() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -346,20 +319,11 @@ public function testSetSslVerifyhost() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -416,20 +380,11 @@ public function testSetCustomHttpHeaders() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -490,20 +445,11 @@ public function testSetCustomHost() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -560,20 +506,11 @@ public function testSetPort() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(3)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], - [ - $this->anything(), - $this->identicalTo(self::DEFAULT_CURL_OPTIONS), - ], - ) + ->willReturnMap([ + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + [$this->anything(), $this->identicalTo($expectedOptions), true], + [$this->anything(), $this->identicalTo(self::DEFAULT_CURL_OPTIONS), true], + ]) ; $curlErrno = $this->getFunctionMock(self::__NAMESPACE__, 'curl_errno'); @@ -628,11 +565,9 @@ public function testCustomPortWillSetFromSchema() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(1)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], + ->with( + $this->anything(), + $this->identicalTo($expectedOptions), ) ; @@ -684,11 +619,9 @@ public function testCustomPortWillSetFromUrl() $curlSetoptArray = $this->getFunctionMock(self::__NAMESPACE__, 'curl_setopt_array'); $curlSetoptArray->expects($this->exactly(1)) - ->withConsecutive( - [ - $this->anything(), - $this->identicalTo($expectedOptions), - ], + ->with( + $this->anything(), + $this->identicalTo($expectedOptions) ) ; diff --git a/tests/Unit/Client/Psr18ClientTest.php b/tests/Unit/Client/Psr18ClientTest.php index 4b028163..20641934 100644 --- a/tests/Unit/Client/Psr18ClientTest.php +++ b/tests/Unit/Client/Psr18ClientTest.php @@ -112,13 +112,12 @@ public function testStartAndStopImpersonateUser() $request = $this->createMock(ServerRequestInterface::class); $request->expects($this->exactly(4)) ->method('withHeader') - ->withConsecutive( - ['X-Redmine-API-Key', 'access_token'], - ['X-Redmine-API-Key', 'access_token'], - ['X-Redmine-Switch-User', 'Sam'], - ['X-Redmine-API-Key', 'access_token'], - ) - ->willReturn($request); + ->willReturnMap([ + ['X-Redmine-API-Key', 'access_token', $request], + ['X-Redmine-API-Key', 'access_token', $request], + ['X-Redmine-Switch-User', 'Sam', $request], + ['X-Redmine-API-Key', 'access_token', $request], + ]); $requestFactory = $this->createMock(ServerRequestFactoryInterface::class); $requestFactory->method('createServerRequest')->willReturn($request); diff --git a/tests/Unit/Serializer/XmlSerializerTest.php b/tests/Unit/Serializer/XmlSerializerTest.php index 201ced73..3c5f0fbb 100644 --- a/tests/Unit/Serializer/XmlSerializerTest.php +++ b/tests/Unit/Serializer/XmlSerializerTest.php @@ -201,24 +201,13 @@ public function createFromArrayDecodesToExpectedString(array $data, $expected) $this->assertSame($expected, trim($dom->saveXML())); } - public static function getInvalidSerializedData(): iterable + public static function getInvalidSerializedData(): array { - if (version_compare(\PHP_VERSION, '8.0.0', '<')) { - // old Exception message for PHP 7.4 - yield [ - 'Could not create XML from array: Undefined index: ', - [], - ]; - } else { - // new Exeption message for PHP 8.0 - yield [ - 'Could not create XML from array: Undefined array key ""', - [], - ]; - } - yield [ - 'Could not create XML from array: String could not be parsed as XML', - ['0' => ['foobar']], + return[ + [ + 'Could not create XML from array: String could not be parsed as XML', + ['0' => ['foobar']], + ] ]; } From 8d83a266854686ed6d238d232309ed5b1f09fc29 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 16:52:06 +0200 Subject: [PATCH 7/9] catch and tests warnings thrown by SimpleXMLElement --- phpunit.xml.dist | 15 ++++++++++- src/Redmine/Serializer/XmlSerializer.php | 28 +++++++++++++++++++-- tests/Unit/Serializer/XmlSerializerTest.php | 24 +++++++++--------- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index edf5b133..40138393 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,18 @@ - + diff --git a/src/Redmine/Serializer/XmlSerializer.php b/src/Redmine/Serializer/XmlSerializer.php index 691d5fb2..1b130407 100644 --- a/src/Redmine/Serializer/XmlSerializer.php +++ b/src/Redmine/Serializer/XmlSerializer.php @@ -65,14 +65,26 @@ private function deserialize(string $encoded): void { $this->encoded = $encoded; + $prevSetting = libxml_use_internal_errors(true); + try { $this->deserialized = new SimpleXMLElement($encoded); } catch (Throwable $e) { + $errors = []; + + foreach (libxml_get_errors() as $error) { + $errors[] = $error->message; + } + + libxml_clear_errors(); + throw new SerializerException( - 'Catched error "' . $e->getMessage() . '" while decoding XML: ' . $encoded, + 'Catched errors: "' . implode('", "', $errors) . '" while decoding XML: ' . $encoded, $e->getCode(), $e ); + } finally { + libxml_use_internal_errors($prevSetting); } $this->normalize($this->deserialized); @@ -95,14 +107,26 @@ private function denormalize(array $normalized): void $rootElementName = array_key_first($this->normalized); + $prevSetting = libxml_use_internal_errors(true); + try { $this->deserialized = $this->createXmlElement($rootElementName, $this->normalized[$rootElementName]); } catch (Throwable $e) { + $errors = []; + + foreach (libxml_get_errors() as $error) { + $errors[] = $error->message; + } + + libxml_clear_errors(); + throw new SerializerException( - 'Could not create XML from array: ' . $e->getMessage(), + 'Could not create XML from array: "' . implode('", "', $errors) . '"', $e->getCode(), $e ); + } finally { + libxml_use_internal_errors($prevSetting); } $this->encoded = $this->deserialized->asXml(); diff --git a/tests/Unit/Serializer/XmlSerializerTest.php b/tests/Unit/Serializer/XmlSerializerTest.php index 3c5f0fbb..3e547955 100644 --- a/tests/Unit/Serializer/XmlSerializerTest.php +++ b/tests/Unit/Serializer/XmlSerializerTest.php @@ -74,24 +74,24 @@ public function createFromStringDecodesToExpectedNormalizedData(string $data, $e public static function getInvalidEncodedData(): array { return [ - [ - 'Catched error "String could not be parsed as XML" while decoding XML: ', + 'empty string' => [ + 'Catched errors: "" while decoding XML: ', '', ], - [ - 'Catched error "String could not be parsed as XML" while decoding XML: ', + 'wrong start tag' => [ + 'Catched errors: "Start tag expected, \'<\' not found'."\n".'" while decoding XML: ', '', ], - [ - 'Catched error "String could not be parsed as XML" while decoding XML: <>', + 'invalid element name as start tag' => [ + 'Catched errors: "StartTag: invalid element name'."\n".'", "Extra content at the end of the document'."\n".'" while decoding XML: <>', '<>', ], - [ - 'Catched error "String could not be parsed as XML" while decoding XML: ', + 'Premature end of data' => [ + 'Catched errors: "Premature end of data in tag a line 1'."\n".'" while decoding XML: ', '', ], - [ - 'Catched error "String could not be parsed as XML" while decoding XML: ', + 'invalid element name as start tag 2' => [ + 'Catched errors: "StartTag: invalid element name'."\n".'", "Extra content at the end of the document'."\n".'" while decoding XML: ', '', ], ]; @@ -204,8 +204,8 @@ public function createFromArrayDecodesToExpectedString(array $data, $expected) public static function getInvalidSerializedData(): array { return[ - [ - 'Could not create XML from array: String could not be parsed as XML', + 'invalid element name as start tag' => [ + 'Could not create XML from array: "StartTag: invalid element name'."\n".'", "Extra content at the end of the document'."\n".'"', ['0' => ['foobar']], ] ]; From 77f13ca44dee33a6137a8b18a05651a5382b2e60 Mon Sep 17 00:00:00 2001 From: Art4 Date: Wed, 13 Sep 2023 17:00:03 +0200 Subject: [PATCH 8/9] Create composer command to create HTML code-coverage report --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 49755dd9..2956c094 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ } }, "scripts": { + "coverage": "phpunit --coverage-html=\".phpunit.cache/code-coverage\"", "test": "phpunit" } } From ed3a1eba19b603935a5a9a2751d6a6723a3b7848 Mon Sep 17 00:00:00 2001 From: Art4 Date: Thu, 14 Sep 2023 08:36:09 +0200 Subject: [PATCH 9/9] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f598152..085d76f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/kbsali/php-redmine-api/compare/v2.2.0...v2.x) +### Added + +- Added support for PHP 8.2 + ### Deprecated - `Redmine\Api\AbstractApi::attachCustomFieldXML()` is deprecated @@ -37,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for PHP 8.1 - New interface `Redmine\Exception` that is implemented by every library-related exception - New exception `Redmine\Exception\ClientException` for client related exceptions - New exception `Redmine\Exception\InvalidApiNameException` if an invalid API instance is requested