diff --git a/src/Runner/Runner.php b/src/Runner/Runner.php index 5d302d2f..77575ed6 100644 --- a/src/Runner/Runner.php +++ b/src/Runner/Runner.php @@ -96,6 +96,7 @@ public function setTempDirectory(?string $path): void } $this->tempDir = $path; + $this->testHandler->setTempDirectory($path); } diff --git a/src/Runner/TestHandler.php b/src/Runner/TestHandler.php index 0dc78cae..27ffdbf5 100644 --- a/src/Runner/TestHandler.php +++ b/src/Runner/TestHandler.php @@ -25,6 +25,9 @@ class TestHandler /** @var Runner */ private $runner; + /** @var string|null */ + private $tempDir; + public function __construct(Runner $runner) { @@ -32,6 +35,12 @@ public function __construct(Runner $runner) } + public function setTempDirectory(?string $path): void + { + $this->tempDir = $path; + } + + public function initiate(string $file): void { [$annotations, $title] = $this->getAnnotations($file); @@ -157,28 +166,59 @@ private function initiateMultiple(Test $test, $count): array private function initiateTestCase(Test $test, $foo, PhpInterpreter $interpreter) { - $job = new Job($test->withArguments(['method' => TestCase::LIST_METHODS]), $interpreter, $this->runner->getEnvironmentVariables()); - $job->run(); - - if (in_array($job->getExitCode(), [Job::CODE_ERROR, Job::CODE_FAIL, Job::CODE_SKIP], true)) { - return $test->withResult($job->getExitCode() === Job::CODE_SKIP ? Test::SKIPPED : Test::FAILED, $job->getTest()->stdout); + $methods = null; + + if ($this->tempDir) { + $cacheFile = $this->tempDir . DIRECTORY_SEPARATOR . 'TestHandler.testCase.' . substr(md5($test->getSignature()), 0, 5) . '.list'; + if (is_file($cacheFile)) { + $cache = unserialize(file_get_contents($cacheFile)); + + $valid = true; + foreach ($cache['files'] as $path => $mTime) { + if (!is_file($path) || filemtime($path) !== $mTime) { + $valid = false; + break; + } + } + if ($valid) { + $methods = $cache['methods']; + } + } } - $stdout = $job->getTest()->stdout; + if ($methods === null) { + $job = new Job($test->withArguments(['method' => TestCase::LIST_METHODS]), $interpreter, $this->runner->getEnvironmentVariables()); + $job->run(); - if (!preg_match('#^TestCase:([^\n]+)$#m', $stdout, $m)) { - return $test->withResult(Test::FAILED, "Cannot list TestCase methods in file '{$test->getFile()}'. Do you call TestCase::run() in it?"); - } - $testCaseClass = $m[1]; + if (in_array($job->getExitCode(), [Job::CODE_ERROR, Job::CODE_FAIL, Job::CODE_SKIP], true)) { + return $test->withResult($job->getExitCode() === Job::CODE_SKIP ? Test::SKIPPED : Test::FAILED, $job->getTest()->stdout); + } + + $stdout = $job->getTest()->stdout; - preg_match_all('#^Method:([^\n]+)$#m', $stdout, $m); - if (count($m[1]) < 1) { - return $test->withResult(Test::SKIPPED, "Class $testCaseClass in file '{$test->getFile()}' does not contain test methods."); + if (!preg_match('#^TestCase:([^\n]+)$#m', $stdout, $m)) { + return $test->withResult(Test::FAILED, "Cannot list TestCase methods in file '{$test->getFile()}'. Do you call TestCase::run() in it?"); + } + $testCaseClass = $m[1]; + + preg_match_all('#^Method:([^\n]+)$#m', $stdout, $m); + if (count($m[1]) < 1) { + return $test->withResult(Test::SKIPPED, "Class $testCaseClass in file '{$test->getFile()}' does not contain test methods."); + } + $methods = $m[1]; + + if ($this->tempDir) { + preg_match_all('#^Dependency:([^\n]+)$#m', $stdout, $m); + file_put_contents($cacheFile, serialize([ + 'methods' => $methods, + 'files' => array_combine($m[1], array_map('filemtime', $m[1])), + ])); + } } return array_map(function (string $method) use ($test): Test { return $test->withArguments(['method' => $method]); - }, $m[1]); + }, $methods); }