Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
54.17% |
13 / 24 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
TestSuiteBuilder | |
54.17% |
13 / 24 |
|
0.00% |
0 / 3 |
19.63 | |
0.00% |
0 / 1 |
sortByTimeDescending | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
3.33 | |||
smallestGroup | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
buildSuites | |
84.62% |
11 / 13 |
|
0.00% |
0 / 1 |
4.06 |
1 | <?php |
2 | |
3 | declare( strict_types = 1 ); |
4 | |
5 | namespace MediaWiki\Composer\PhpUnitSplitter; |
6 | |
7 | /** |
8 | * @license GPL-2.0-or-later |
9 | */ |
10 | class TestSuiteBuilder { |
11 | |
12 | private static function sortByTimeDescending( TestDescriptor $a, TestDescriptor $b ): int { |
13 | if ( $a->getDuration() === $b->getDuration() ) { |
14 | return 0; |
15 | } |
16 | return ( $a->getDuration() > $b->getDuration() ? -1 : 1 ); |
17 | } |
18 | |
19 | private static function smallestGroup( array $suites ): int { |
20 | $min = 10000; |
21 | $minIndex = 0; |
22 | $groups = count( $suites ); |
23 | for ( $i = 0; $i < $groups; $i++ ) { |
24 | if ( $suites[$i]["time"] < $min ) { |
25 | $min = $suites[$i]["time"]; |
26 | $minIndex = $i; |
27 | } |
28 | } |
29 | return $minIndex; |
30 | } |
31 | |
32 | public function buildSuites( array $testDescriptors, int $groups ): array { |
33 | $suites = array_fill( 0, $groups, [ "list" => [], "time" => 0 ] ); |
34 | $roundRobin = 0; |
35 | usort( $testDescriptors, [ self::class, "sortByTimeDescending" ] ); |
36 | foreach ( $testDescriptors as $testDescriptor ) { |
37 | if ( !$testDescriptor->getFilename() ) { |
38 | // We didn't resolve a matching file for this test, so we skip it |
39 | // from the suite here. This only happens for "known" missing test |
40 | // classes (see PhpUnitXmlManager::EXPECTED_MISSING_CLASSES) - in |
41 | // all other cases a missing test file will throw an exception during |
42 | // suite building. |
43 | continue; |
44 | } |
45 | if ( $testDescriptor->getDuration() === 0 ) { |
46 | // If no explicit timing information is available for a test, we just |
47 | // drop it round-robin into the next bucket. |
48 | $nextSuite = $roundRobin; |
49 | $roundRobin = ( $roundRobin + 1 ) % $groups; |
50 | } else { |
51 | // If we have information about the test duration, we try and balance |
52 | // out the tests suites by having an even amount of time spent on |
53 | // each suite. |
54 | $nextSuite = self::smallestGroup( $suites ); |
55 | } |
56 | $suites[$nextSuite]["list"][] = $testDescriptor->getFilename(); |
57 | $suites[$nextSuite]["time"] += $testDescriptor->getDuration(); |
58 | } |
59 | return $suites; |
60 | } |
61 | } |