Skip to content

Commit

Permalink
Improve parallel test run
Browse files Browse the repository at this point in the history
  • Loading branch information
gehrisandro committed Nov 27, 2023
1 parent 0f6683f commit 03ef783
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 14 deletions.
2 changes: 0 additions & 2 deletions WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
- [ ] Awesome docs

# Known Bugs
- [ ] Make test filtering more stable. For example "for all .ch domains" does not work
- [ ] Fix parallel for Laravel (set proper ENV variables)

# Backlog Prio 1
- [ ] Properly support xdebug
Expand Down
20 changes: 15 additions & 5 deletions src/MutationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Pest\Mutate;

use ParaTest\Options;
use Pest\Mutate\Event\Facade;
use Pest\Mutate\Plugins\Mutate;
use Pest\Mutate\Repositories\ConfigurationRepository;
Expand Down Expand Up @@ -37,7 +38,7 @@ public function updateResult(MutationTestResult $result): void
* @param array<string, array<int, array<int, string>>> $coveredLines
* @param array<int, string> $originalArguments
*/
public function start(array $coveredLines, Configuration $configuration, array $originalArguments): bool
public function start(array $coveredLines, Configuration $configuration, array $originalArguments, int $processId = null): bool
{
// TODO: we should pass the tests to run in another way, maybe via cache, mutation or env variable
$filters = [];
Expand All @@ -61,17 +62,26 @@ public function start(array $coveredLines, Configuration $configuration, array $
return false;
}

$envs = [
Mutate::ENV_MUTATION_TESTING => $this->mutation->file->getRealPath(),
Mutate::ENV_MUTATION_FILE => $this->mutation->modifiedSourcePath,
];

if ($processId !== null) {
$envs['PARATEST'] = '1';
$envs[Options::ENV_KEY_TOKEN] = $processId;
$envs[Options::ENV_KEY_UNIQUE_TOKEN] = uniqid($processId.'_');
$envs['LARAVEL_PARALLEL_TESTING'] = 1;
}

// TODO: filter arguments to remove unnecessary stuff (Teamcity, Coverage, etc.)
$process = new Process(
command: [
...$originalArguments,
'--bail',
'--filter="'.implode('|', $filters).'"',
],
env: [
Mutate::ENV_MUTATION_TESTING => $this->mutation->file->getRealPath(),
Mutate::ENV_MUTATION_FILE => $this->mutation->modifiedSourcePath,
],
env: $envs,
timeout: $this->calculateTimeout(),
);

Expand Down
20 changes: 13 additions & 7 deletions src/Tester/MutationTestRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MutationTestRunner implements MutationTestRunnerContract
private array $originalArguments;

/**
* @var array<int, MutationTest>
* @var array<int, ?MutationTest>
*/
private array $runningTests;

Expand Down Expand Up @@ -248,26 +248,28 @@ private function runTestsInParallel(MutationSuite $mutationSuite, array $covered
}
}

$this->runningTests = [];
$this->runningTests = array_fill(1, $processes, null);

foreach ($tests as $test) {
if ($this->stop) {
break;
}

while (count($this->runningTests) >= $processes) {
while (count(array_filter($this->runningTests, fn (?MutationTest $process): bool => $process instanceof MutationTest)) >= $processes) {
if ($this->checkRunningTestsHaveFinished()) {
continue;
}

usleep(1000);
}

if ($test->start($coveredLines, $this->getConfiguration(), $this->originalArguments)) {
$this->runningTests[] = $test;
$processId = (int) array_key_first(array_filter($this->runningTests, fn (?MutationTest $process): bool => ! $process instanceof MutationTest));
if ($test->start($coveredLines, $this->getConfiguration(), $this->originalArguments, $processId)) {
$this->runningTests[$processId] = $test;
}
}

while (! $this->stop && $this->runningTests !== []) {
while (! $this->stop && array_filter($this->runningTests, fn (?MutationTest $process): bool => $process instanceof MutationTest) !== []) {
$this->checkRunningTestsHaveFinished();
}
}
Expand Down Expand Up @@ -297,8 +299,12 @@ private function runTests(MutationSuite $mutationSuite, array $coveredLines): vo
private function checkRunningTestsHaveFinished(): bool
{
foreach ($this->runningTests as $index => $runningTest) {
if (! $runningTest instanceof MutationTest) {
continue;
}

if ($runningTest->hasFinished()) {
unset($this->runningTests[$index]);
$this->runningTests[$index] = null;

return true;
}
Expand Down

0 comments on commit 03ef783

Please sign in to comment.