From 2a2a2039f1da4d85cd7f7e1fb93bf37384a775e2 Mon Sep 17 00:00:00 2001 From: Alexey Kopytko Date: Mon, 14 Sep 2020 21:14:00 +0900 Subject: [PATCH] Test for Engine (#1330) * Test for Engine * Add test for InitialTestsFailed * Test for successful run * Fix MetricsCalculator --- src/Engine.php | 64 +++-- src/Metrics/MinMsiChecker.php | 3 +- src/Mutation/MutationGenerator.php | 3 +- src/Process/Runner/InitialTestsFailed.php | 10 +- src/Process/Runner/InitialTestsRunner.php | 3 +- src/Process/Runner/MutationTestingRunner.php | 3 +- src/Resource/Memory/MemoryLimiter.php | 3 +- .../Coverage/CoverageChecker.php | 3 +- .../TestFrameworkExtraOptionsFilter.php | 3 +- .../ProjectCode/ProjectCodeProvider.php | 2 - tests/phpunit/EngineTest.php | 263 ++++++++++++++++++ 11 files changed, 329 insertions(+), 31 deletions(-) create mode 100644 tests/phpunit/EngineTest.php diff --git a/src/Engine.php b/src/Engine.php index 791bfdc438..d05ebf5a99 100644 --- a/src/Engine.php +++ b/src/Engine.php @@ -45,6 +45,7 @@ use Infection\Metrics\MinMsiChecker; use Infection\Metrics\MinMsiCheckFailed; use Infection\Mutation\MutationGenerator; +use Infection\PhpParser\Visitor\IgnoreNode\NodeIgnorer; use Infection\Process\Runner\InitialTestsFailed; use Infection\Process\Runner\InitialTestsRunner; use Infection\Process\Runner\MutationTestingRunner; @@ -128,41 +129,70 @@ private function runInitialTestSuite(): void return; } - $initialTestSuitProcess = $this->initialTestsRunner->run( + $initialTestSuiteProcess = $this->initialTestsRunner->run( $this->config->getTestFrameworkExtraOptions(), - explode(' ', (string) $this->config->getInitialTestsPhpOptions()), + $this->getInitialTestsPhpOptionsArray(), $this->config->shouldSkipCoverage() ); - if (!$initialTestSuitProcess->isSuccessful()) { - throw InitialTestsFailed::fromProcessAndAdapter($initialTestSuitProcess, $this->adapter); + if (!$initialTestSuiteProcess->isSuccessful()) { + throw InitialTestsFailed::fromProcessAndAdapter($initialTestSuiteProcess, $this->adapter); } $this->coverageChecker->checkCoverageHasBeenGenerated( - $initialTestSuitProcess->getCommandLine(), - $initialTestSuitProcess->getOutput() + $initialTestSuiteProcess->getCommandLine(), + $initialTestSuiteProcess->getOutput() ); - // Limit the memory used for the mutation processes based on the memory used for the initial - // test run - $this->memoryLimiter->limitMemory($initialTestSuitProcess->getOutput(), $this->adapter); + /* + * Limit the memory used for the mutation processes based on the memory + * used for the initial test run. + */ + $this->memoryLimiter->limitMemory($initialTestSuiteProcess->getOutput(), $this->adapter); + } + + /** + * @return string[] + */ + private function getInitialTestsPhpOptionsArray(): array + { + return explode(' ', (string) $this->config->getInitialTestsPhpOptions()); } private function runMutationAnalysis(): void { $mutations = $this->mutationGenerator->generate( $this->config->mutateOnlyCoveredCode(), - $this->adapter instanceof IgnoresAdditionalNodes - ? $this->adapter->getNodeIgnorers() - : [] + $this->getNodeIgnorers() + ); + + $this->mutationTestingRunner->run( + $mutations, + $this->getFilteredExtraOptionsForMutant() ); + } - $actualExtraOptions = $this->config->getTestFrameworkExtraOptions(); + /** + * @return NodeIgnorer[] + */ + private function getNodeIgnorers(): array + { + if ($this->adapter instanceof IgnoresAdditionalNodes) { + return $this->adapter->getNodeIgnorers(); + } - $filteredExtraOptionsForMutant = $this->adapter instanceof ProvidesInitialRunOnlyOptions - ? $this->testFrameworkExtraOptionsFilter->filterForMutantProcess($actualExtraOptions, $this->adapter->getInitialRunOnlyOptions()) - : $actualExtraOptions; + return []; + } + + private function getFilteredExtraOptionsForMutant(): string + { + if ($this->adapter instanceof ProvidesInitialRunOnlyOptions) { + return $this->testFrameworkExtraOptionsFilter->filterForMutantProcess( + $this->config->getTestFrameworkExtraOptions(), + $this->adapter->getInitialRunOnlyOptions() + ); + } - $this->mutationTestingRunner->run($mutations, $filteredExtraOptionsForMutant); + return $this->config->getTestFrameworkExtraOptions(); } } diff --git a/src/Metrics/MinMsiChecker.php b/src/Metrics/MinMsiChecker.php index b1fe3356ef..6dd36d493c 100644 --- a/src/Metrics/MinMsiChecker.php +++ b/src/Metrics/MinMsiChecker.php @@ -39,8 +39,9 @@ /** * @internal + * @final */ -final class MinMsiChecker +class MinMsiChecker { private const VALUE_OVER_REQUIRED_TOLERANCE = 2; diff --git a/src/Mutation/MutationGenerator.php b/src/Mutation/MutationGenerator.php index 279619c350..f492e03948 100644 --- a/src/Mutation/MutationGenerator.php +++ b/src/Mutation/MutationGenerator.php @@ -49,8 +49,9 @@ /** * @internal + * @final */ -final class MutationGenerator +class MutationGenerator { private $traceProvider; private $mutators; diff --git a/src/Process/Runner/InitialTestsFailed.php b/src/Process/Runner/InitialTestsFailed.php index 3912b10722..76cc74cdde 100644 --- a/src/Process/Runner/InitialTestsFailed.php +++ b/src/Process/Runner/InitialTestsFailed.php @@ -47,18 +47,18 @@ final class InitialTestsFailed extends Exception { public static function fromProcessAndAdapter( - Process $initialTestSuitProcess, + Process $initialTestSuiteProcess, TestFrameworkAdapter $testFrameworkAdapter ): self { $testFrameworkKey = $testFrameworkAdapter->getName(); $lines = [ 'Project tests must be in a passing state before running Infection.', - $testFrameworkAdapter->getInitialTestsFailRecommendations($initialTestSuitProcess->getCommandLine()), + $testFrameworkAdapter->getInitialTestsFailRecommendations($initialTestSuiteProcess->getCommandLine()), sprintf( '%s reported an exit code of %d.', $testFrameworkKey, - $initialTestSuitProcess->getExitCode() + $initialTestSuiteProcess->getExitCode() ), sprintf( 'Refer to the %s\'s output below:', @@ -66,12 +66,12 @@ public static function fromProcessAndAdapter( ), ]; - if ($stdOut = $initialTestSuitProcess->getOutput()) { + if ($stdOut = $initialTestSuiteProcess->getOutput()) { $lines[] = 'STDOUT:'; $lines[] = $stdOut; } - if ($stdError = $initialTestSuitProcess->getErrorOutput()) { + if ($stdError = $initialTestSuiteProcess->getErrorOutput()) { $lines[] = 'STDERR:'; $lines[] = $stdError; } diff --git a/src/Process/Runner/InitialTestsRunner.php b/src/Process/Runner/InitialTestsRunner.php index ba4eeb370f..9326053f8b 100644 --- a/src/Process/Runner/InitialTestsRunner.php +++ b/src/Process/Runner/InitialTestsRunner.php @@ -44,8 +44,9 @@ /** * @internal + * @final */ -final class InitialTestsRunner +class InitialTestsRunner { private $processBuilder; private $eventDispatcher; diff --git a/src/Process/Runner/MutationTestingRunner.php b/src/Process/Runner/MutationTestingRunner.php index 8837ad53b8..5081e5e5ba 100644 --- a/src/Process/Runner/MutationTestingRunner.php +++ b/src/Process/Runner/MutationTestingRunner.php @@ -50,8 +50,9 @@ /** * @internal + * @final */ -final class MutationTestingRunner +class MutationTestingRunner { private $processFactory; private $mutantFactory; diff --git a/src/Resource/Memory/MemoryLimiter.php b/src/Resource/Memory/MemoryLimiter.php index 8547ae7137..149d5d2483 100644 --- a/src/Resource/Memory/MemoryLimiter.php +++ b/src/Resource/Memory/MemoryLimiter.php @@ -44,8 +44,9 @@ /** * @internal + * @final */ -final class MemoryLimiter +class MemoryLimiter { private $fileSystem; private $phpIniPath; diff --git a/src/TestFramework/Coverage/CoverageChecker.php b/src/TestFramework/Coverage/CoverageChecker.php index d972023597..43d9368fe1 100644 --- a/src/TestFramework/Coverage/CoverageChecker.php +++ b/src/TestFramework/Coverage/CoverageChecker.php @@ -50,8 +50,9 @@ /** * @internal + * @final */ -final class CoverageChecker +class CoverageChecker { private const PHPUNIT = 'phpunit'; private const CODECEPTION = 'codeception'; diff --git a/src/TestFramework/TestFrameworkExtraOptionsFilter.php b/src/TestFramework/TestFrameworkExtraOptionsFilter.php index 417d5ff4e8..3fff1ec39a 100644 --- a/src/TestFramework/TestFrameworkExtraOptionsFilter.php +++ b/src/TestFramework/TestFrameworkExtraOptionsFilter.php @@ -42,8 +42,9 @@ /** * @internal + * @final */ -final class TestFrameworkExtraOptionsFilter +class TestFrameworkExtraOptionsFilter { /** * @param string[] $initialRunOnlyOptions diff --git a/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php b/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php index 3c2b8cf4b6..fddad26611 100644 --- a/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php +++ b/tests/phpunit/AutoReview/ProjectCode/ProjectCodeProvider.php @@ -50,7 +50,6 @@ use Infection\Console\OutputFormatter\OutputFormatter; use Infection\Console\OutputFormatter\ProgressFormatter; use Infection\Console\XdebugHandler; -use Infection\Engine; use Infection\Event\Subscriber\MutationGeneratingConsoleLoggerSubscriber; use Infection\Event\Subscriber\NullSubscriber; use Infection\FileSystem\DummyFileSystem; @@ -101,7 +100,6 @@ final class ProjectCodeProvider MutationGeneratingConsoleLoggerSubscriber::class, TestFrameworkTypes::class, NodeMutationGenerator::class, - Engine::class, NonExecutableFinder::class, AdapterInstaller::class, DetectionStatus::class, diff --git a/tests/phpunit/EngineTest.php b/tests/phpunit/EngineTest.php new file mode 100644 index 0000000000..bbbc4d6526 --- /dev/null +++ b/tests/phpunit/EngineTest.php @@ -0,0 +1,263 @@ +createMock(Configuration::class); + $config + ->expects($this->once()) + ->method('shouldSkipInitialTests') + ->willReturn(false) + ; + + $adapter = $this->createMock(TestFrameworkAdapter::class); + $adapter + ->expects($this->once()) + ->method('getName') + ->willReturn('foo') + ; + + $coverageChecker = $this->createMock(CoverageChecker::class); + $coverageChecker->expects($this->never())->method($this->anything()); + + $eventDispatcher = $this->createMock(EventDispatcher::class); + $eventDispatcher->expects($this->never())->method($this->anything()); + + $process = $this->createMock(Process::class); + $process + ->expects($this->once()) + ->method('isSuccessful') + ->willReturn(false) + ; + + $process + ->expects($this->once()) + ->method('getCommandLine') + ->willReturn('/tmp/bar') + ; + + $initialTestsRunner = $this->createMock(InitialTestsRunner::class); + $initialTestsRunner + ->expects($this->once()) + ->method('run') + ->willReturn($process) + ; + + $memoryLimiter = $this->createMock(MemoryLimiter::class); + $memoryLimiter->expects($this->never())->method($this->anything()); + + $mutationGenerator = $this->createMock(MutationGenerator::class); + $mutationGenerator->expects($this->never())->method($this->anything()); + + $mutationTestingRunner = $this->createMock(MutationTestingRunner::class); + $mutationTestingRunner->expects($this->never())->method($this->anything()); + + $minMsiChecker = $this->createMock(MinMsiChecker::class); + $minMsiChecker->expects($this->never())->method($this->anything()); + + $consoleOutput = $this->createMock(ConsoleOutput::class); + $consoleOutput->expects($this->never())->method($this->anything()); + + $metricsCalculator = $this->createMock(MetricsCalculator::class); + $metricsCalculator->expects($this->never())->method($this->anything()); + + $testFrameworkExtraOptionsFilter = $this->createMock(TestFrameworkExtraOptionsFilter::class); + $testFrameworkExtraOptionsFilter->expects($this->never())->method($this->anything()); + + $engine = new Engine( + $config, + $adapter, + $coverageChecker, + $eventDispatcher, + $initialTestsRunner, + $memoryLimiter, + $mutationGenerator, + $mutationTestingRunner, + $minMsiChecker, + $consoleOutput, + $metricsCalculator, + $testFrameworkExtraOptionsFilter + ); + + $this->expectException(InitialTestsFailed::class); + + $engine->execute(); + } + + public function test_initial_test_run_succeeds(): void + { + $config = $this->createMock(Configuration::class); + $config + ->expects($this->once()) + ->method('shouldSkipInitialTests') + ->willReturn(false) + ; + + $adapter = $this->createMock(TestFrameworkAdapter::class); + $adapter->expects($this->never())->method($this->anything()); + + $coverageChecker = $this->createMock(CoverageChecker::class); + $coverageChecker + ->expects($this->once()) + ->method('checkCoverageHasBeenGenerated') + ; + + $eventDispatcher = $this->createMock(EventDispatcher::class); + $eventDispatcher + ->expects($this->once()) + ->method('dispatch') + ->with($this->callback(static function (ApplicationExecutionWasFinished $event) { + return true; + })); + + $process = $this->createMock(Process::class); + $process + ->expects($this->once()) + ->method('isSuccessful') + ->willReturn(true) + ; + + $process + ->expects($this->once()) + ->method('getCommandLine') + ->willReturn('/tmp/bar') + ; + + $process + ->expects($this->exactly(2)) + ->method('getOutput') + ->willReturn('testing') + ; + + $initialTestsRunner = $this->createMock(InitialTestsRunner::class); + $initialTestsRunner + ->expects($this->once()) + ->method('run') + ->willReturn($process) + ; + + $memoryLimiter = $this->createMock(MemoryLimiter::class); + $memoryLimiter + ->expects($this->once()) + ->method('limitMemory') + ->with('testing', $adapter) + ; + + $mutationGenerator = $this->createMock(MutationGenerator::class); + $mutationGenerator + ->expects($this->once()) + ->method('generate') + ->with(false, []) + ; + + $mutationTestingRunner = $this->createMock(MutationTestingRunner::class); + $mutationTestingRunner + ->expects($this->once()) + ->method('run') + ->with($this->callback(static function (iterable $input) { + return true; + })) + ; + + $consoleOutput = $this->createMock(ConsoleOutput::class); + $consoleOutput->expects($this->never())->method($this->anything()); + + $minMsiChecker = $this->createMock(MinMsiChecker::class); + $minMsiChecker + ->expects($this->once()) + ->method('checkMetrics') + ->with(1000, 2.0, 3.0, $consoleOutput) + ; + + $metricsCalculator = $this->createMock(MetricsCalculator::class); + $metricsCalculator + ->expects($this->once()) + ->method('getTestedMutantsCount') + ->willReturn(1000) + ; + $metricsCalculator + ->expects($this->once()) + ->method('getMutationScoreIndicator') + ->willReturn(2.0) + ; + $metricsCalculator + ->expects($this->once()) + ->method('getCoveredCodeMutationScoreIndicator') + ->willReturn(3.0) + ; + + $testFrameworkExtraOptionsFilter = $this->createMock(TestFrameworkExtraOptionsFilter::class); + $testFrameworkExtraOptionsFilter->expects($this->never())->method($this->anything()); + + $engine = new Engine( + $config, + $adapter, + $coverageChecker, + $eventDispatcher, + $initialTestsRunner, + $memoryLimiter, + $mutationGenerator, + $mutationTestingRunner, + $minMsiChecker, + $consoleOutput, + $metricsCalculator, + $testFrameworkExtraOptionsFilter + ); + + $engine->execute(); + } +}