diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fda8b6d..bd6c80b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,9 @@ jobs: - php: "8.0" - php: "8.1" - php: "8.2" + - php: "8.3" + - php: "8.4" + - php: "8.5" steps: - name: Checkout diff --git a/composer b/composer index 9508812..0a926db 100755 --- a/composer +++ b/composer @@ -118,9 +118,19 @@ if (!class_exists('ComposerWrapper')) { $modified = clone $now; //hack: DateTime $modifier argument validation. Taken from https://stackoverflow.com/a/34899724/2165434 - $success = @$modified->modify($updateFreq); + try { + $success = @$modified->modify($updateFreq); + } catch (\Exception $e) { + $success = false; + } if ($success === false) { - throw new \Exception(sprintf('Wrong update frequency is requested: %s; should be valid DateTime modifier (follow %s for the help)', $updateFreq,'https://www.php.net/manual/en/datetime.modify.php')); + throw new \Exception( + sprintf( + 'Wrong update frequency is requested: %s; should be valid DateTime modifier (follow %s for the help)', + $updateFreq, + 'https://www.php.net/manual/en/datetime.modify.php' + ) + ); } //endhack @@ -178,13 +188,20 @@ if (!class_exists('ComposerWrapper')) { */ protected $params; - public function __construct(ComposerWrapperParams $params = null) + public function __construct($params = null) { - if (null === $params ) { + if (null === $params) { $this->params = new ComposerWrapperParams(); $this->params->loadReal(); - } else { + } elseif ($params instanceof ComposerWrapperParams) { $this->params = $params; + } else { + throw new \InvalidArgumentException( + sprintf( + "%s class can only be instantiated with ComposerWrapperParams object", + __CLASS__ + ) + ); } } diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index 661b6b0..5f21729 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -20,7 +20,9 @@ private function getExpectedShebang() public static function callNonPublic($object, $method, $args) { $method = new ReflectionMethod($object, $method); - $method->setAccessible(true); + if (PHP_VERSION_ID < 80500) { + $method->setAccessible(true); + } return $method->invokeArgs($object, $args); } @@ -28,7 +30,9 @@ public static function callNonPublic($object, $method, $args) protected static function setNonPublic($object, $property, $arg) { $property = new ReflectionProperty($object, $property); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80500) { + $property->setAccessible(true); + } $property->setValue($object, $arg); } diff --git a/tests/ComposerWrapperParamsTest.php b/tests/ComposerWrapperParamsTest.php index 49f42bf..bcb8509 100644 --- a/tests/ComposerWrapperParamsTest.php +++ b/tests/ComposerWrapperParamsTest.php @@ -132,10 +132,9 @@ public function setComposerDirHandlesGoodValues() self::assertSame(__DIR__, $actual); } - public function composerDirBadDataProvider() + public static function composerDirBadDataProvider() { return array( - "access denied" => array('/var'), "doesn't exists" => array(__DIR__ . '/i_dont_exist'), "non dir" => array(__FILE__), ); @@ -152,6 +151,20 @@ public function setComposerDirThrowsOnBadValues($input) self::callNonPublic($params, 'setComposerDir', array($input)); } + /** + * @test + */ + public function setComposerDirThrowsOnNotWritableDirectory() + { + $root = vfsStream::setup(); + $dir = vfsStream::newDirectory('readonly', 0555); + $root->addChild($dir); + + $this->expectExceptionMessageRegExpCompat('\Exception', '/Wrong composer dir is requested:.*/'); + $params = new ComposerWrapperParams(); + self::callNonPublic($params, 'setComposerDir', array($dir->url())); + } + /** * @return ComposerWrapperParams */ diff --git a/tests/ComposerWrapperTest.php b/tests/ComposerWrapperTest.php index 13637e7..9556e79 100644 --- a/tests/ComposerWrapperTest.php +++ b/tests/ComposerWrapperTest.php @@ -17,6 +17,48 @@ private static function getInstance() return new $class; } + /** + * @test + */ + public function acceptsParameters() + { + try { + new ComposerWrapper(new ComposerWrapperParams()); + new ComposerWrapper(); + new ComposerWrapper(null); + $passed = true; + } catch (Exception $e) { + $passed = false; + } + self::assertTrue($passed); + } + + /** + * @test + * @dataProvider invalidConstructorArguments + */ + public function throwsOnInvalidParameters(array $args) + { + $this->expectExceptionMessageCompat( + 'InvalidArgumentException', + 'ComposerWrapper class can only be instantiated with ComposerWrapperParams object' + ); + $class = new ReflectionClass('ComposerWrapper'); + $class->newInstanceArgs($args); + } + + public static function invalidConstructorArguments() + { + return array( + array(array('test')), + array(array(new \stdClass())), + array(array(true)), + array(array(false)), + array(array(123)), + array(array(123.45)), + ); + } + /** * @test */ @@ -386,7 +428,7 @@ public function selfUpdateWorks() ->willReturnCallback(function ($command, &$exitCode) { $exitCode = 0; }); self::callNonPublic($wrapper, 'selfUpdate', array($file->url())); - clearstatcache(null, $file->url()); + clearstatcache(true, $file->url()); $this->assertGreaterThanOrEqual($now->getTimestamp(), filemtime($file->url())); } @@ -555,7 +597,9 @@ public function passThroughWrapperWorksWithReferences() $exitCode = null; $class = new ReflectionClass($wrapper); $method = $class->getMethod('passthru'); - $method->setAccessible(true); + if (PHP_VERSION_ID < 80500) { + $method->setAccessible(true); + } $method->invokeArgs( $wrapper, array( diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8d5cfbf..e4cd03c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,6 +3,32 @@ require __DIR__ . '/BaseTestCase.php'; // output buffer manipulations to get rid of shebang (#!/usr/bin/env php) +$bootstrapFailureMessages = array(); + +set_error_handler(function ($severity, $message, $file, $line) use (&$bootstrapFailureMessages) { + $bootstrapFailureMessages[] = sprintf('PHP Error: %s in %s:%d', $message, $file, $line); + + return true; +}); + +set_exception_handler(function ($exception) use (&$bootstrapFailureMessages) { + $bootstrapFailureMessages[] = sprintf( + 'Uncaught %s: %s in %s:%d', + get_class($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine() + ); +}); + ob_start(); require __DIR__ . '/../composer'; ob_end_clean(); + +restore_exception_handler(); +restore_error_handler(); + +if (!empty($bootstrapFailureMessages)) { + fwrite(STDERR, implode(PHP_EOL, $bootstrapFailureMessages) . PHP_EOL); + exit(1); +}