Skip to content

Commit 9fe666a

Browse files
authored
Merge pull request #270 from magento-commerce/ACQE-4538
ACQE-4538: Allow mftf generate minimum possible groups runnable by co…
2 parents 5238edb + ae8661e commit 9fe666a

File tree

2 files changed

+115
-27
lines changed

2 files changed

+115
-27
lines changed

dev/tests/unit/Magento/FunctionalTestFramework/Util/Sorter/ParallelGroupSorterTest.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,45 @@ public function testTestsAndSuitesSplitByGroupNumberSuiteNoTest(): void
420420
}
421421
}
422422

423+
/**
424+
* Test splitting into minimum groups.
425+
*
426+
* @return void
427+
*/
428+
public function testSplitMinGroups(): void
429+
{
430+
// mock tests for test object handler.
431+
$this->createMockForTest(0);
432+
433+
// create test to size array
434+
$sampleTestArray = [
435+
'test1' => 1,
436+
'test2' => 125,
437+
'test3' => 35
438+
];
439+
// create mock suite references
440+
$sampleSuiteArray = [
441+
'mockSuite1' => ['mockTest1', 'mockTest2'],
442+
'mockSuite2' => ['mockTest3', 'mockTest4'],
443+
];
444+
445+
// perform sort
446+
$testSorter = new ParallelGroupSorter();
447+
$actualResult = $testSorter->getTestsGroupedByFixedGroupCount($sampleSuiteArray, $sampleTestArray, 3);
448+
// verify the resulting groups
449+
$this->assertCount(3, $actualResult);
450+
451+
$expectedResults = [
452+
1 => ['test2', 'test3', 'test1'],
453+
2 => ['mockSuite1'],
454+
3 => ['mockSuite2'],
455+
];
456+
457+
foreach ($actualResult as $groupNum => $group) {
458+
$this->assertEquals($expectedResults[$groupNum], array_keys($group));
459+
}
460+
}
461+
423462
/**
424463
* Test splitting tests and suites with invalid group number.
425464
*
@@ -438,15 +477,16 @@ public function testTestsAndSuitesSplitByInvalidGroupNumber(): void
438477
];
439478
// create mock suite references
440479
$sampleSuiteArray = [
441-
'mockSuite1' => ['mockTest1', 'mockTest2']
480+
'mockSuite1' => ['mockTest1', 'mockTest2'],
481+
'mockSuite2' => ['mockTest3', 'mockTest4'],
442482
];
443483

444484
$this->expectException(FastFailException::class);
445-
$this->expectExceptionMessage("Invalid parameter 'groupTotal': must be equal or greater than 2");
485+
$this->expectExceptionMessage("Invalid parameter 'groupTotal': must be equal or greater than 3");
446486

447487
// perform sort
448488
$testSorter = new ParallelGroupSorter();
449-
$testSorter->getTestsGroupedByFixedGroupCount($sampleSuiteArray, $sampleTestArray, 1);
489+
$testSorter->getTestsGroupedByFixedGroupCount($sampleSuiteArray, $sampleTestArray, 2);
450490
}
451491

452492
/**

src/Magento/FunctionalTestingFramework/Util/Sorter/ParallelGroupSorter.php

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function __construct()
3737
public function getTestsGroupedBySize($suiteConfiguration, $testNameToSize, $time)
3838
{
3939
// we must have the lines argument in order to create the test groups
40-
if ($time === 0) {
40+
if ($time == 0) {
4141
throw new FastFailException(
4242
"Please provide the argument '--time' to the robo command in order to".
4343
" generate grouped tests manifests for a parallel execution"
@@ -86,27 +86,38 @@ public function getTestsGroupedBySize($suiteConfiguration, $testNameToSize, $tim
8686
*/
8787
public function getTestsGroupedByFixedGroupCount($suiteConfiguration, $testNameToSize, $groupTotal)
8888
{
89-
$suiteNameToTestSize = $this->getSuiteNameToTestSize($suiteConfiguration);
90-
91-
$minRequiredGroupCount = count($suiteNameToTestSize);
92-
if (!empty($testNameToSize)) {
93-
$minRequiredGroupCount += 1;
94-
}
95-
if ($groupTotal < $minRequiredGroupCount) {
96-
throw new FastFailException(
97-
"Invalid parameter 'groupTotal': must be equal or greater than {$minRequiredGroupCount}"
98-
);
99-
}
100-
10189
if (empty($suiteConfiguration)) {
10290
return $this->convertArrayIndexStartingAtOne($this->splitTestsIntoGroups($testNameToSize, $groupTotal));
10391
}
10492

93+
$suiteNameToTestSize = $this->getSuiteNameToTestSize($suiteConfiguration);
94+
10595
// Calculate suite group totals
10696
$suiteNameToGroupCount = $this->getSuiteGroupCounts($suiteNameToTestSize, $testNameToSize, $groupTotal);
10797

108-
// Calculate test group total
109-
$testGroupTotal = $groupTotal - array_sum($suiteNameToGroupCount);
98+
$suitesGroupTotal = array_sum($suiteNameToGroupCount);
99+
100+
// Calculate minimum required groups
101+
$minSuiteGroupTotal = count($suiteNameToTestSize);
102+
$minTestGroupTotal = empty($testNameToSize) ? 0 : 1;
103+
$minRequiredGroupTotal = $minSuiteGroupTotal + $minTestGroupTotal;
104+
105+
if ($groupTotal < $minRequiredGroupTotal) {
106+
throw new FastFailException(
107+
"Invalid parameter 'groupTotal': must be equal or greater than {$minRequiredGroupTotal}"
108+
);
109+
} elseif ($groupTotal < $suitesGroupTotal + $minTestGroupTotal) {
110+
// Split in savvy mode when $groupTotal requested is very small
111+
$testGroupTotal = $minTestGroupTotal;
112+
// Reduce suite group total
113+
$suiteNameToGroupCount = $this->reduceSuiteGroupTotal(
114+
$suiteNameToGroupCount,
115+
$groupTotal - $minTestGroupTotal
116+
);
117+
} else {
118+
// Calculate test group total
119+
$testGroupTotal = $groupTotal - $suitesGroupTotal;
120+
}
110121

111122
// Split tests and suites
112123
$testGroups = $this->splitTestsIntoGroups($testNameToSize, $testGroupTotal);
@@ -140,19 +151,19 @@ private function getSuiteGroupCounts($suiteNameToTestSize, $testNameToSize, $gro
140151
$maxSuiteTime = max($suiteNameToSize);
141152

142153
// Calculate 2 possible suite group times
143-
$ceilSuiteGroupNumber = ceil($maxSuiteTime / $minGroupTime);
154+
$ceilSuiteGroupNumber = (int)ceil($maxSuiteTime / $minGroupTime);
144155
$ceilSuiteGroupTime = max(ceil($maxSuiteTime / $ceilSuiteGroupNumber), $minGroupTime);
145-
$floorSuiteGroupNumber = floor($maxSuiteTime / $minGroupTime);
146-
if ($floorSuiteGroupNumber !== 0) {
156+
$floorSuiteGroupNumber = (int)floor($maxSuiteTime / $minGroupTime);
157+
if ($floorSuiteGroupNumber != 0) {
147158
$floorSuiteGroupTime = max(ceil($maxSuiteTime / $floorSuiteGroupNumber), $minGroupTime);
148159
}
149160

150161
// Calculate test group time for ceiling
151162
$ceilSuiteNameToGroupCount = $this->getSuiteGroupCountFromGroupTime($suiteNameToTestSize, $ceilSuiteGroupTime);
152163
$ceilSuiteGroupTotal = array_sum($ceilSuiteNameToGroupCount);
153-
$ceilTestGroupTotal = (int) $groupTotal - (int) $ceilSuiteGroupTotal;
164+
$ceilTestGroupTotal = $groupTotal - $ceilSuiteGroupTotal;
154165

155-
if ($ceilTestGroupTotal === 0) {
166+
if ($ceilTestGroupTotal == 0) {
156167
$ceilTestGroupTime = 0;
157168
} else {
158169
$ceilTestGroupTime = ceil(array_sum($testNameToSize) / $ceilTestGroupTotal);
@@ -161,15 +172,15 @@ private function getSuiteGroupCounts($suiteNameToTestSize, $testNameToSize, $gro
161172
// Set suite group total to ceiling
162173
$suiteNameToGroupCount = $ceilSuiteNameToGroupCount;
163174

164-
if (isset($floorSuiteGroupTime) && $ceilSuiteGroupTime !== $floorSuiteGroupTime) {
175+
if (isset($floorSuiteGroupTime) && $ceilSuiteGroupTime != $floorSuiteGroupTime) {
165176
// Calculate test group time for floor
166177
$floorSuiteNameToGroupCount = $this->getSuiteGroupCountFromGroupTime(
167178
$suiteNameToTestSize,
168179
$floorSuiteGroupTime
169180
);
170181
$floorSuiteGroupTotal = array_sum($floorSuiteNameToGroupCount);
171182
$floorTestGroupTotal = $groupTotal - $floorSuiteGroupTotal;
172-
if ($floorTestGroupTotal === 0) {
183+
if ($floorTestGroupTotal == 0) {
173184
$floorTestGroupTime = 0;
174185
} else {
175186
$floorTestGroupTime = ceil(array_sum($testNameToSize) / $floorTestGroupTotal);
@@ -187,6 +198,39 @@ private function getSuiteGroupCounts($suiteNameToTestSize, $testNameToSize, $gro
187198
return $suiteNameToGroupCount;
188199
}
189200

201+
/**
202+
* Reduce total suite groups to a given $total.
203+
* This method will reduce 1 from a suite that's greater than 1 repeatedly until sum of all groups reaches $total.
204+
*
205+
* @param array $suiteNameToGroupCount
206+
* @param integer $total
207+
* @return array
208+
* @throws FastFailException
209+
*/
210+
private function reduceSuiteGroupTotal($suiteNameToGroupCount, $total)
211+
{
212+
if (count($suiteNameToGroupCount) > $total) {
213+
throw new FastFailException(
214+
"Invalid parameter 'total': must be equal or greater than {count($suiteNameToGroupCount)}"
215+
);
216+
}
217+
218+
$done = false;
219+
while (!$done) {
220+
foreach ($suiteNameToGroupCount as $suite => $count) {
221+
if (array_sum($suiteNameToGroupCount) == $total) {
222+
$done = true;
223+
break;
224+
}
225+
if ($count > 1) {
226+
$suiteNameToGroupCount[$suite] -= 1;
227+
}
228+
}
229+
}
230+
231+
return $suiteNameToGroupCount;
232+
}
233+
190234
/**
191235
* Return array contains suitename to number of groups to be split based on time.
192236
*
@@ -203,7 +247,7 @@ private function getSuiteGroupCountFromGroupTime($suiteNameToTestSize, $time)
203247
if ($suiteTime <= $time) {
204248
$suiteNameToGroupCount[$suiteName] = 1;
205249
} else {
206-
$suiteNameToGroupCount[$suiteName] = min(ceil($suiteTime/$time), $maxCount);
250+
$suiteNameToGroupCount[$suiteName] = min((int)ceil($suiteTime/$time), $maxCount);
207251
}
208252
}
209253
return $suiteNameToGroupCount;
@@ -218,6 +262,10 @@ private function getSuiteGroupCountFromGroupTime($suiteNameToTestSize, $time)
218262
*/
219263
private function splitTestsIntoGroups($tests, $groupCnt)
220264
{
265+
if (empty($tests)) {
266+
return [];
267+
}
268+
221269
// Reverse sort the test array by size
222270
uasort($tests, function ($a, $b) {
223271
return $a >= $b ? -1 : 1;
@@ -247,7 +295,7 @@ private function splitSuitesIntoGroups($suiteNameToTestSize, $suiteNameToGroupCo
247295
$groups = [];
248296
foreach ($suiteNameToTestSize as $suiteName => $suiteTests) {
249297
$suiteCnt = $suiteNameToGroupCount[$suiteName];
250-
if ($suiteCnt === 1) {
298+
if ($suiteCnt == 1) {
251299
$groups[][$suiteName] = array_sum($suiteTests);
252300
$this->addSuiteToConfig($suiteName, null, $suiteTests);
253301
} elseif ($suiteCnt > 1) {

0 commit comments

Comments
 (0)