diff --git a/src/Report/Clover.php b/src/Report/Clover.php index e0f893ce2..2ee42bd8f 100644 --- a/src/Report/Clover.php +++ b/src/Report/Clover.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report; +use PHPUnit\Framework\Error\Warning; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Node\File; use SebastianBergmann\CodeCoverage\RuntimeException; @@ -19,7 +20,7 @@ final class Clover { /** - * @throws \RuntimeException + * @throws RuntimeException */ public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { @@ -235,13 +236,13 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string if ($target !== null) { if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + throw new RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); } if (@\file_put_contents($target, $buffer) === false) { throw new RuntimeException( \sprintf( - 'Could not write to "%s', + 'Could not write to "%s"', $target ) ); @@ -253,6 +254,10 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string private function createDirectory(string $directory): bool { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + try { + return !(!\is_dir($directory) && !\mkdir($directory, 0777, true) && !\is_dir($directory)); + } catch (Warning $warning) { + return false; + } } } diff --git a/src/Report/Crap4j.php b/src/Report/Crap4j.php index 6713be0c8..5f1a7d2be 100644 --- a/src/Report/Crap4j.php +++ b/src/Report/Crap4j.php @@ -26,7 +26,7 @@ public function __construct(int $threshold = 30) } /** - * @throws \RuntimeException + * @throws RuntimeException */ public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { @@ -117,13 +117,13 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string if ($target !== null) { if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + throw new RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); } if (@\file_put_contents($target, $buffer) === false) { throw new RuntimeException( \sprintf( - 'Could not write to "%s', + 'Could not write to "%s"', $target ) ); diff --git a/src/Report/Html/Facade.php b/src/Report/Html/Facade.php index 318b49a5a..e6e36c666 100644 --- a/src/Report/Html/Facade.php +++ b/src/Report/Html/Facade.php @@ -49,7 +49,6 @@ public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, s /** * @throws RuntimeException * @throws \InvalidArgumentException - * @throws \RuntimeException */ public function process(CodeCoverage $coverage, string $target): void { @@ -94,7 +93,7 @@ public function process(CodeCoverage $coverage, string $target): void if ($node instanceof DirectoryNode) { if (!$this->createDirectory($target . $id)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id)); + throw new RuntimeException(\sprintf('Directory "%s" was not created', $target . $id)); } $directory->render($node, $target . $id . '/index.html'); @@ -103,7 +102,7 @@ public function process(CodeCoverage $coverage, string $target): void $dir = \dirname($target . $id); if (!$this->createDirectory($dir)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir)); + throw new RuntimeException(\sprintf('Directory "%s" was not created', $dir)); } $file->render($node, $target . $id . '.html'); diff --git a/src/Report/PHP.php b/src/Report/PHP.php index 73e2f4ddf..d9ce76dad 100644 --- a/src/Report/PHP.php +++ b/src/Report/PHP.php @@ -18,7 +18,7 @@ final class PHP { /** - * @throws \SebastianBergmann\CodeCoverage\RuntimeException + * @throws RuntimeException */ public function process(CodeCoverage $coverage, ?string $target = null): string { @@ -41,13 +41,13 @@ public function process(CodeCoverage $coverage, ?string $target = null): string if ($target !== null) { if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + throw new RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); } if (@\file_put_contents($target, $buffer) === false) { throw new RuntimeException( \sprintf( - 'Could not write to "%s', + 'Could not write to "%s"', $target ) ); diff --git a/tests/_files/BankAccount.php.txt b/tests/_files/BankAccount.php.txt new file mode 100644 index 000000000..40546d107 --- /dev/null +++ b/tests/_files/BankAccount.php.txt @@ -0,0 +1,77 @@ +setData(array ( + '%s%etests/_files/BankAccount.php' => + array ( + 8 => + array ( + 0 => 'BankAccountTest::testBalanceIsInitiallyZero', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 9 => NULL, + 13 => + array ( + ), + 14 => + array ( + ), + 15 => + array ( + ), + 16 => + array ( + ), + 18 => + array ( + ), + 22 => + array ( + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 24 => + array ( + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 25 => NULL, + 29 => + array ( + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative', + 1 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 31 => + array ( + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 32 => NULL, + ), +)); +$coverage->setTests(array ( + 'BankAccountTest::testBalanceIsInitiallyZero' => + array ( + 'size' => 'unknown', + 'status' => -1, + ), + 'BankAccountTest::testBalanceCannotBecomeNegative' => + array ( + 'size' => 'unknown', + 'status' => -1, + ), + 'BankAccountTest::testBalanceCannotBecomeNegative2' => + array ( + 'size' => 'unknown', + 'status' => -1, + ), + 'BankAccountTest::testDepositWithdrawMoney' => + array ( + 'size' => 'unknown', + 'status' => -1, + ), +)); + +$filter = $coverage->filter(); +$filter->setWhitelistedFiles(array ( + '%s%etests/_files/BankAccount.php' => true, +)); + +return $coverage; \ No newline at end of file diff --git a/tests/tests/CloverTest.php b/tests/tests/CloverTest.php index 7fdbf7da6..e482ebd7d 100644 --- a/tests/tests/CloverTest.php +++ b/tests/tests/CloverTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report; +use SebastianBergmann\CodeCoverage\RuntimeException; use SebastianBergmann\CodeCoverage\TestCase; /** @@ -45,4 +46,22 @@ public function testCloverForClassWithAnonymousFunction(): void $clover->process($this->getCoverageForClassWithAnonymousFunction()) ); } + + public function testCloverThrowsRuntimeExceptionWhenUnableToWriteToTarget(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not write to "stdout://"'); + + $clover = new Clover; + $clover->process($this->getCoverageForBankAccount(), 'stdout://'); + } + + public function testCloverThrowsRuntimeExceptionWhenTargetDirCouldNotBeCreated(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Directory "/foo/bar" was not created'); + + $clover = new Clover; + $clover->process($this->getCoverageForBankAccount(), '/foo/bar/baz'); + } } diff --git a/tests/tests/Crap4jTest.php b/tests/tests/Crap4jTest.php index 033fe4c74..bde3a9423 100644 --- a/tests/tests/Crap4jTest.php +++ b/tests/tests/Crap4jTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report; +use SebastianBergmann\CodeCoverage\RuntimeException; use SebastianBergmann\CodeCoverage\TestCase; /** @@ -45,4 +46,22 @@ public function testForClassWithAnonymousFunction(): void $crap4j->process($this->getCoverageForClassWithAnonymousFunction(), null, 'CoverageForClassWithAnonymousFunction') ); } + + public function testCrap4jThrowsRuntimeExceptionWhenUnableToWriteToTarget(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not write to "stdout://"'); + + $Crap4j = new Crap4j; + $Crap4j->process($this->getCoverageForBankAccount(), 'stdout://'); + } + + public function testCrap4jThrowsRuntimeExceptionWhenTargetDirWasNotCreated(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Directory "/foo/bar" was not created'); + + $Crap4j = new Crap4j; + $Crap4j->process($this->getCoverageForBankAccount(), '/foo/bar/baz'); + } } diff --git a/tests/tests/HTMLTest.php b/tests/tests/HTMLTest.php index 0ddd85d66..cdae3a960 100644 --- a/tests/tests/HTMLTest.php +++ b/tests/tests/HTMLTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use SebastianBergmann\CodeCoverage\RuntimeException; use SebastianBergmann\CodeCoverage\TestCase; class HTMLTest extends TestCase @@ -69,6 +70,15 @@ public function testForClassWithAnonymousFunction(): void $this->assertFilesEquals($expectedFilesPath, self::$TEST_TMP_PATH); } + public function testThrowsRuntimeExceptionWhenTargetDirDoesNotExist(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Directory "/foo/bar/baz/" does not exist.'); + + $report = new Facade(); + $report->process($this->getCoverageForBankAccount(), '/foo/bar/baz'); + } + /** * @param string $expectedFilesPath * @param string $actualFilesPath diff --git a/tests/tests/PHPTest.php b/tests/tests/PHPTest.php new file mode 100644 index 000000000..6eefed1f9 --- /dev/null +++ b/tests/tests/PHPTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\RuntimeException; +use SebastianBergmann\CodeCoverage\TestCase; + +/** + * @covers SebastianBergmann\CodeCoverage\Report\PHP + */ +class PHPTest extends TestCase +{ + public function testPHPForBankAccountTest(): void + { + $report = new PHP(); + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'BankAccount.php.txt', + \str_replace(\PHP_EOL, "\n", $report->process($this->getCoverageForBankAccount())) + ); + } + + public function testReportThrowsRuntimeExceptionWhenUnableToWriteToTarget(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not write to "stdin://"'); + + $report = new PHP(); + $report->process($this->getCoverageForBankAccount(), 'stdin://'); + } + + public function testReportThrowsRuntimeExceptionWhenTargetDirCouldNotBeCreated(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Directory "/foo/bar" was not created'); + + $report = new PHP(); + $report->process($this->getCoverageForBankAccount(), '/foo/bar/baz'); + } +} diff --git a/tests/tests/XmlTest.php b/tests/tests/XmlTest.php index 13045a74c..d3e547e3d 100644 --- a/tests/tests/XmlTest.php +++ b/tests/tests/XmlTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Xml; +use SebastianBergmann\CodeCoverage\RuntimeException; use SebastianBergmann\CodeCoverage\TestCase; class XmlTest extends TestCase @@ -30,7 +31,11 @@ protected function tearDown(): void foreach ($tmpFilesIterator as $path => $fileInfo) { /* @var \SplFileInfo $fileInfo */ - \unlink($fileInfo->getPathname()); + if (!\is_dir($fileInfo->getPathname())) { + \unlink($fileInfo->getPathname()); + } else { + \rmdir($fileInfo->getPathname()); + } } } @@ -64,6 +69,27 @@ public function testForClassWithAnonymousFunction(): void $this->assertFilesEquals($expectedFilesPath, self::$TEST_TMP_PATH); } + public function testReportThrowsRuntimeExceptionWhenUnableToCreateTargetDir(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage("'/foo/bar/baz/' could not be created."); + + $xml = new Facade('1.0.0'); + $xml->process($this->getCoverageForBankAccount(), '/foo/bar/baz'); + } + + public function testReportThrowsRuntimeExceptionWhenUnableToWriteToTargetDir(): void + { + $target = self::$TEST_TMP_PATH . '/non-writable-dir'; + @\mkdir($target, 0555); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage("'$target/' exists but is not writable"); + + $xml = new Facade('1.0.0'); + $xml->process($this->getCoverageForBankAccount(), $target); + } + /** * @param string $expectedFilesPath * @param string $actualFilesPath