From 6aed8e689ce18ae30e2fdd9607741b543865cf2e Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Wed, 27 Sep 2023 09:37:47 +1000 Subject: [PATCH 1/4] Split by filename --- src/SplitterCommand.php | 19 +++++++++++++----- src/TestMapper.php | 27 ++++++++++++++++--------- tests/PhpUnitSplitterTest.php | 38 +++++++---------------------------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/SplitterCommand.php b/src/SplitterCommand.php index f0b194e..e2389ff 100644 --- a/src/SplitterCommand.php +++ b/src/SplitterCommand.php @@ -21,9 +21,11 @@ class SplitterCommand extends Command { protected function configure(): void { $this->addArgument('splits', InputArgument::OPTIONAL, "The number of splits", 1); $this->addArgument('index', InputArgument::OPTIONAL, "The index of the current split", 0); - $this->addOption('tests-file', 't', InputOption::VALUE_REQUIRED, "The xml file listing all tests.", getcwd() . './tests.xml'); + $this->addOption('tests-file', 't', InputOption::VALUE_REQUIRED, "The xml file listing all tests.", getcwd() . '/tests.xml'); $this->addOption('results-file', 'f', InputOption::VALUE_REQUIRED, "The results cache file.", getcwd() . '/.phpunit.result.cache', ); $this->addOption('bootstrap-file', 'b', InputOption::VALUE_OPTIONAL, "The tests bootstrap file.", getcwd() . '/tests/bootstrap.php'); + $this->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, "The prefix to remove from the file names.", getcwd() . '/'); + $this->addOption('json', 'j', InputOption::VALUE_NONE, "Output the result as json."); } /** @@ -39,15 +41,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int $index = (int) $input->getArgument('index'); $testsFile = $input->getOption('tests-file'); $resultsFile = $input->getOption('results-file'); + $prefix = $input->getOption('prefix'); + $json = $input->getOption('json'); - $mapper = new TestMapper($testsFile, $resultsFile); + $mapper = new TestMapper($testsFile, $resultsFile, $prefix); $map = $mapper->sortMap($mapper->getMap()); - foreach ($this->split($map, $splits, $index) as $testName => $test) { - $output->writeln(\addslashes($testName) . ' ' . $test['path']); + $split = $this->split($map, $splits, $index); + if ($json) { + $output->writeln(\json_encode($split)); + return Command::SUCCESS; + } + foreach ($split as $testPath => $test) { + $output->writeln($testPath); } - return 0; + return Command::SUCCESS; } private function split(array $map, int $splits, int $index): array { diff --git a/src/TestMapper.php b/src/TestMapper.php index 17f4c43..c2349eb 100644 --- a/src/TestMapper.php +++ b/src/TestMapper.php @@ -15,9 +15,12 @@ final class TestMapper { private \SimpleXMLElement|FALSE $testsXml; private TestResultCache $resultCache; - public function __construct(string $testListFilePath, string $testResultFilePath) { + private string $prefix; + + public function __construct(string $testListFilePath, string $testResultFilePath, string $prefix) { $this->testsXml = \simplexml_load_file($testListFilePath); $this->resultCache = new DefaultTestResultCache($testResultFilePath); + $this->prefix = $prefix; } public function getMap(): array { @@ -28,23 +31,29 @@ public function getMap(): array { $className = (string) $class->attributes()['name']; try { $reflection = new \ReflectionClass($className); - } - catch (\ReflectionException $e) { + } catch (\ReflectionException $e) { // Couldn't find the class. continue; } $filename = $reflection->getFileName(); + $filename = \str_replace($this->prefix, '', $filename); + $map[$filename] = [ + 'className' => $className, + 'time' => 0.0, + ]; $testCases = $class->xpath('testCaseMethod'); foreach ($testCases as $testCase) { - $testName = $reflection->getShortName() . '::' . $testCase->attributes()['name']; + $shortName = (string) $testCase->attributes()['name']; + $fullName = $reflection->getName() . '::' . $shortName; $dataSet = $testCase->attributes()['dataSet'] ?? NULL; + $cacheKey = $fullName; if ($dataSet !== NULL) { - $testName .= " with data set $dataSet"; + $cacheKey .= " with data set $dataSet"; + $shortName .= "@$dataSet"; } - $map[$testName] = [ - 'path' => $filename, - 'time' => $this->resultCache->getTime($testName), - ]; + $time = $this->resultCache->getTime($cacheKey); + $map[$filename][$shortName] = ['time' => $time]; + $map[$filename]['time'] += $time; } } return $map; diff --git a/tests/PhpUnitSplitterTest.php b/tests/PhpUnitSplitterTest.php index 38de73f..454852c 100644 --- a/tests/PhpUnitSplitterTest.php +++ b/tests/PhpUnitSplitterTest.php @@ -11,44 +11,20 @@ class PhpUnitSplitterTest extends TestCase { public function testSplitter(): void { $fixtures = dirname(__DIR__) . '/tests/fixtures'; - $mapper = new TestMapper("$fixtures/tests.xml", "$fixtures/.phpunit.result.cache"); + $mapper = new TestMapper("$fixtures/tests.xml", "$fixtures/.phpunit.result.cache", \getcwd() . '/'); $map = $mapper->getMap(); $this->assertSame([ - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\FastTestsTest::testOne', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\FastTestsTest::testTwo', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\FastTestsTest::testThree', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\FastTestsTest::testFour', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\FastTestsTest::testFive', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\ProviderTest::testProvider with data set "one"', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\ProviderTest::testProvider with data set "two"', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\ProviderTest::testProvider with data set "three"', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\ProviderTest::testProvider with data set "four"', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\ProviderTest::testProvider with data set "five"', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\SlowTestsTest::testOne', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\SlowTestsTest::testTwo', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\SlowTestsTest::testThree', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\SlowTestsTest::testFour', - 'PhpUnitSplitter\\Tests\\fixtures\\Test\\SlowTestsTest::testFive', + 'fixtures/Test/FastTestsTest.php', + 'fixtures/Test/ProviderTest.php', + 'fixtures/Test/SlowTestsTest.php', ], array_keys($map)); $sorted = $mapper->sortMap($map); $this->assertSame([ - 'PhpUnitSplitter\Tests\fixtures\Test\FastTestsTest::testOne', - 'PhpUnitSplitter\Tests\fixtures\Test\FastTestsTest::testTwo', - 'PhpUnitSplitter\Tests\fixtures\Test\FastTestsTest::testThree', - 'PhpUnitSplitter\Tests\fixtures\Test\FastTestsTest::testFour', - 'PhpUnitSplitter\Tests\fixtures\Test\FastTestsTest::testFive', - 'PhpUnitSplitter\Tests\fixtures\Test\SlowTestsTest::testOne', - 'PhpUnitSplitter\Tests\fixtures\Test\ProviderTest::testProvider with data set "one"', - 'PhpUnitSplitter\Tests\fixtures\Test\SlowTestsTest::testTwo', - 'PhpUnitSplitter\Tests\fixtures\Test\ProviderTest::testProvider with data set "three"', - 'PhpUnitSplitter\Tests\fixtures\Test\SlowTestsTest::testThree', - 'PhpUnitSplitter\Tests\fixtures\Test\ProviderTest::testProvider with data set "four"', - 'PhpUnitSplitter\Tests\fixtures\Test\SlowTestsTest::testFour', - 'PhpUnitSplitter\Tests\fixtures\Test\ProviderTest::testProvider with data set "two"', - 'PhpUnitSplitter\Tests\fixtures\Test\SlowTestsTest::testFive', - 'PhpUnitSplitter\Tests\fixtures\Test\ProviderTest::testProvider with data set "five"', + 'fixtures/Test/FastTestsTest.php', + 'fixtures/Test/SlowTestsTest.php', + 'fixtures/Test/ProviderTest.php', ], array_keys($sorted)); } From ad7dae6ae408446ec95613e2285650c3f7289caf Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Wed, 27 Sep 2023 09:40:09 +1000 Subject: [PATCH 2/4] Remove phpunit-finder dep --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 432db01..94c7651 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,6 @@ "php": "^8.1", "ext-simplexml": "*", "phpunit/phpunit": "^9.6", - "previousnext/phpunit-finder": "^2.0", "symfony/console": "^6.3" }, "autoload": { From 55915413a887216ec5dbe555b902fd27f80e41c5 Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Wed, 27 Sep 2023 10:11:50 +1000 Subject: [PATCH 3/4] Update src/TestMapper.php Co-authored-by: Michael Strelan --- src/TestMapper.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/TestMapper.php b/src/TestMapper.php index c2349eb..3c09b8b 100644 --- a/src/TestMapper.php +++ b/src/TestMapper.php @@ -36,7 +36,9 @@ public function getMap(): array { continue; } $filename = $reflection->getFileName(); - $filename = \str_replace($this->prefix, '', $filename); + if (\str_starts_with($filename, $this->prefix)) { + $filename = \substr($filename, \strlen($this->prefix)); + } $map[$filename] = [ 'className' => $className, 'time' => 0.0, From 43e8ea1adcd8a9d020b44c2f8c5507ee5330d579 Mon Sep 17 00:00:00 2001 From: Kim Pepper Date: Wed, 27 Sep 2023 10:11:33 +1000 Subject: [PATCH 4/4] Update readme --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fc5872e..d993b90 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,12 @@ phpunit-splitter 4 2 --tests-file=tests.xml --results-file=.phpunit.result.cache Pass the results to PHPUnit: ```bash -./phpunit-splitter 2 0 --tests-file=tests/fixtures/tests.xml --results-file=tests/fixtures/.phpunit.result.cache | while IFS= read -r line; do - filepath=$(echo "$line" | awk '{print $NF}') - testname=$(echo "$line" | awk '{$NF=""; print $0}') - ./vendor/bin/phpunit --filter="$testname" $filepath +./phpunit-splitter 2 0 --tests-file=tests/fixtures/tests.xml --results-file=tests/fixtures/.phpunit.result.cache | xargs ./vendor/bin/phpunit done ``` + +Output the test list as JSON: + +```bash +./phpunit-splitter 2 0 --json --tests-file=tests/fixtures/tests.xml --results-file=tests/fixtures/.phpunit.result.cache +```