diff --git a/devTools/phpstan-src-baseline.neon b/devTools/phpstan-src-baseline.neon index 91cb14cb78..687b28cb7f 100644 --- a/devTools/phpstan-src-baseline.neon +++ b/devTools/phpstan-src-baseline.neon @@ -305,6 +305,11 @@ parameters: count: 1 path: ../src/TestFramework/Coverage/XmlReport/XPathFactory.php + - + message: "#^Method Infection\\\\TestFramework\\\\PhpUnit\\\\Adapter\\\\PestAdapterFactory\\:\\:create\\(\\) has parameter \\$sourceDirectories with no value type specified in iterable type array\\.$#" + count: 1 + path: ../src/TestFramework/PhpUnit/Adapter/PestAdapterFactory.php + - message: "#^Only booleans are allowed in an if condition, int given\\.$#" count: 3 diff --git a/src/TestFramework/PhpUnit/Adapter/PestAdapter.php b/src/TestFramework/PhpUnit/Adapter/PestAdapter.php index 68a77f5bdd..77cd463926 100644 --- a/src/TestFramework/PhpUnit/Adapter/PestAdapter.php +++ b/src/TestFramework/PhpUnit/Adapter/PestAdapter.php @@ -35,13 +35,20 @@ namespace Infection\TestFramework\PhpUnit\Adapter; +use Infection\AbstractTestFramework\MemoryUsageAware; use Infection\AbstractTestFramework\TestFrameworkAdapter; +use Infection\PhpParser\Visitor\IgnoreNode\PhpUnitCodeCoverageAnnotationIgnorer; +use Infection\TestFramework\IgnoresAdditionalNodes; +use Infection\TestFramework\ProvidesInitialRunOnlyOptions; use function Safe\preg_match; -use function sprintf; +use function Safe\sprintf; -class PestAdapter implements TestFrameworkAdapter +/** + * @internal + */ +final class PestAdapter implements IgnoresAdditionalNodes, MemoryUsageAware, ProvidesInitialRunOnlyOptions, TestFrameworkAdapter { - private const NAME = 'pest'; + private const NAME = 'Pest'; private PhpUnitAdapter $phpUnitAdapter; @@ -101,4 +108,25 @@ public function getInitialTestsFailRecommendations(string $commandLine): string { return $this->phpUnitAdapter->getInitialTestsFailRecommendations($commandLine); } + + public function getMemoryUsed(string $output): float + { + return $this->phpUnitAdapter->getMemoryUsed($output); + } + + /** + * @return PhpUnitCodeCoverageAnnotationIgnorer[] + */ + public function getNodeIgnorers(): array + { + return [new PhpUnitCodeCoverageAnnotationIgnorer()]; + } + + /** + * @return string[] + */ + public function getInitialRunOnlyOptions(): array + { + return $this->phpUnitAdapter->getInitialRunOnlyOptions(); + } } diff --git a/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php b/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php index 28779020ad..bc9adb3a77 100644 --- a/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php +++ b/src/TestFramework/PhpUnit/Config/Builder/MutationConfigBuilder.php @@ -115,7 +115,6 @@ public function build( $this->setCustomBootstrapPath($customAutoloadFilePath, $xPath); $this->setFilteredTestsToRun($tests, $dom, $xPath); - // todo where is caching? we had it before I guess file_put_contents( $customAutoloadFilePath, $this->createCustomAutoloadWithInterceptor( diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterFactoryTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterFactoryTest.php new file mode 100644 index 0000000000..43066aa5b3 --- /dev/null +++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterFactoryTest.php @@ -0,0 +1,71 @@ +assertSame('Pest', $adapter->getName()); + } + + public function test_it_has_a_name(): void + { + $this->assertSame('pest', PestAdapterFactory::getAdapterName()); + } + + public function test_it_has_an_executable(): void + { + $this->assertSame('pest', PestAdapterFactory::getExecutableName()); + } +} diff --git a/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php new file mode 100644 index 0000000000..38787733ba --- /dev/null +++ b/tests/phpunit/TestFramework/PhpUnit/Adapter/PestAdapterTest.php @@ -0,0 +1,300 @@ +pcovDirectoryProvider = $this->createMock(PCOVDirectoryProvider::class); + $this->initialConfigBuilder = $this->createMock(InitialConfigBuilder::class); + $this->mutationConfigBuilder = $this->createMock(MutationConfigBuilder::class); + $this->cliArgumentsBuilder = $this->createMock(CommandLineArgumentsAndOptionsBuilder::class); + $this->commandLineBuilder = $this->createMock(CommandLineBuilder::class); + + $this->adapter = new PestAdapter( + new PhpUnitAdapter( + '/path/to/pest', + '/tmp', + '/tmp/infection/junit.xml', + $this->pcovDirectoryProvider, + $this->initialConfigBuilder, + $this->mutationConfigBuilder, + $this->cliArgumentsBuilder, + new VersionParser(), + $this->commandLineBuilder, + '1.1.0' + ) + ); + } + + public function test_it_has_a_name(): void + { + $this->assertSame('Pest', $this->adapter->getName()); + } + + public function test_it_supports_junit_reports(): void + { + $this->assertTrue($this->adapter->hasJUnitReport()); + } + + /** + * @dataProvider outputProvider + */ + public function test_it_can_tell_the_outcome_of_the_tests_from_the_output( + string $output, + bool $expected + ): void { + $actual = $this->adapter->testsPass($output); + + $this->assertSame($expected, $actual); + } + + /** + * @dataProvider memoryReportProvider + */ + public function test_it_can_tell_the_memory_usage_from_the_output(string $output, float $expectedResult): void + { + $result = $this->adapter->getMemoryUsed($output); + + $this->assertSame($expectedResult, $result); + } + + public function test_it_provides_initial_run_only_options(): void + { + $options = $this->adapter->getInitialRunOnlyOptions(); + + $this->assertSame( + ['--configuration', '--filter', '--testsuite'], + $options + ); + } + + public function test_it_provides_node_ignorers(): void + { + $nodeIgnorers = array_map('get_class', $this->adapter->getNodeIgnorers()); + + $this->assertSame( + [PhpUnitCodeCoverageAnnotationIgnorer::class], + $nodeIgnorers + ); + } + + /** + * @group integration + */ + public function test_it_provides_initial_test_run_command_line_when_no_coverage_is_expected(): void + { + $this->cliArgumentsBuilder + ->expects($this->once()) + ->method('build') + ->with('', '--group=default') + ; + + $this->commandLineBuilder + ->expects($this->once()) + ->method('build') + ->with('/path/to/pest', ['-d', 'memory_limit=-1'], []) + ->willReturn(['/path/to/pest', '--dummy-argument']) + ; + + $this->pcovDirectoryProvider + ->expects($this->never()) + ->method($this->anything()) + ; + + $initialTestRunCommandLine = $this->adapter->getInitialTestRunCommandLine('--group=default', ['-d', 'memory_limit=-1'], true); + + $this->assertSame( + [ + '/path/to/pest', + '--dummy-argument', + ], + $initialTestRunCommandLine + ); + } + + /** + * @group integration + */ + public function test_it_provides_initial_test_run_command_line_when_coverage_report_is_requested(): void + { + $this->cliArgumentsBuilder + ->expects($this->once()) + ->method('build') + ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') + ->willReturn([ + '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', + ]) + ; + + $this->commandLineBuilder + ->expects($this->once()) + ->method('build') + ->with('/path/to/pest', ['-d', 'memory_limit=-1'], [ + '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', + ]) + ->willReturn([ + '/path/to/pest', + '--group=default', + '--coverage-xml=/tmp/coverage-xml', + '--log-junit=/tmp/infection/junit.xml', + ]) + ; + + $this->pcovDirectoryProvider + ->expects($this->once()) + ->method('shallProvide') + ->willReturn(false) + ; + + $this->pcovDirectoryProvider + ->expects($this->never()) + ->method('getDirectory') + ; + + $initialTestRunCommandLine = $this->adapter->getInitialTestRunCommandLine('--group=default', ['-d', 'memory_limit=-1'], false); + + $this->assertSame( + [ + '/path/to/pest', + '--group=default', + '--coverage-xml=/tmp/coverage-xml', + '--log-junit=/tmp/infection/junit.xml', + ], + $initialTestRunCommandLine + ); + } + + /** + * @group integration + */ + public function test_it_provides_initial_test_run_command_line_when_coverage_report_is_requested_and_pcov_is_in_use(): void + { + $this->cliArgumentsBuilder + ->expects($this->once()) + ->method('build') + ->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml') + ->willReturn([ + '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', + ]) + ; + + $this->commandLineBuilder + ->expects($this->once()) + ->method('build') + ->with('/path/to/pest', [ + '-d', + 'memory_limit=-1', + '-d', + '\\' === DIRECTORY_SEPARATOR ? 'pcov.directory="."' : "pcov.directory='.'", + ], [ + '--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml', + ]) + ->willReturn([ + '/path/to/pest', + '--group=default', + '--coverage-xml=/tmp/coverage-xml', + '--log-junit=/tmp/infection/junit.xml', + ]) + ; + + $this->pcovDirectoryProvider + ->expects($this->once()) + ->method('shallProvide') + ->willReturn(true) + ; + + $this->pcovDirectoryProvider + ->expects($this->once()) + ->method('getDirectory') + ->willReturn('.') + ; + + $initialTestRunCommandLine = $this->adapter->getInitialTestRunCommandLine('--group=default', ['-d', 'memory_limit=-1'], false); + + $this->assertSame( + [ + '/path/to/pest', + '--group=default', + '--coverage-xml=/tmp/coverage-xml', + '--log-junit=/tmp/infection/junit.xml', + ], + $initialTestRunCommandLine + ); + } + + public function outputProvider(): iterable + { + yield ['Tests: 1 risked', true]; + + yield ['Tests: 9 passed', true]; + + yield ['Tests: 1 failed, 8 passed', false]; + } + + public function memoryReportProvider(): iterable + { + yield ['Memory: 8.00MB', 8.0]; + + yield ['Memory: 68.00MB', 68.0]; + + yield ['Memory: 68.00 MB', 68.0]; + + yield ['Time: 2.51 seconds', -1.0]; + } +}