Skip to content

Commit dd7ff14

Browse files
committed
MQE-893: Add flag to robo generate: tests which accepts a specific set of tests to execute
- refactor the custom test configuration to use tests/suites in the global scope
1 parent c204f13 commit dd7ff14

File tree

10 files changed

+267
-175
lines changed

10 files changed

+267
-175
lines changed

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,6 @@ public function testSortWithSuites()
7676

7777
AspectMock::double(TestObjectHandler::class, ['getInstance' => $mockHandler])->make();
7878

79-
// mock a suite object
80-
$mockSuite = AspectMock::double(SuiteObject::class, [
81-
'getBeforeHook' => null,
82-
'getAfterHook' => null,
83-
'getName' => 'mockSuite1'
84-
])->make();
85-
$mockSuiteHandler = AspectMock::double(SuiteObjectHandler::class, ['getObject' => $mockSuite])->make();
86-
AspectMock::double(SuiteObjectHandler::class, ['getInstance' => $mockSuiteHandler]);
87-
8879
// create test to size array
8980
$sampleTestArray = [
9081
'test1' => 100,
@@ -96,8 +87,7 @@ public function testSortWithSuites()
9687

9788
// create mock suite references
9889
$sampleSuiteArray = [
99-
'mockTest1' => ['mockSuite1'],
100-
'mockTest2' => ['mockSuite1']
90+
'mockSuite1' => ['mockTest1', 'mockTest2']
10191
];
10292

10393
// perform sort

src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ public function getAllObjects()
7878
return $this->suiteObjects;
7979
}
8080

81+
/**
82+
* Function which return all tests referenced by suites.
83+
*
84+
* @return array
85+
*/
86+
public function getAllTestReferences()
87+
{
88+
$testsReferencedInSuites = [];
89+
$suites = $this->getAllObjects();
90+
91+
foreach ($suites as $suite) {
92+
/** @var SuiteObject $suite */
93+
$test_keys = array_keys($suite->getTests());
94+
$testToSuiteName = array_fill_keys($test_keys, [$suite->getName()]);
95+
$testsReferencedInSuites = array_merge_recursive($testsReferencedInSuites, $testToSuiteName);
96+
}
97+
98+
return $testsReferencedInSuites;
99+
}
100+
81101
/**
82102
* Method to parse all suite data xml into objects.
83103
*

src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66

77
namespace Magento\FunctionalTestingFramework\Suite;
88

9-
use Magento\Framework\Phrase;
10-
use Magento\Framework\Validator\Exception;
119
use Magento\FunctionalTestingFramework\Suite\Generators\GroupClassGenerator;
1210
use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler;
1311
use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject;
12+
use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler;
1413
use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil;
1514
use Magento\FunctionalTestingFramework\Util\Manifest\BaseTestManifest;
16-
use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest;
1715
use Magento\FunctionalTestingFramework\Util\TestGenerator;
1816
use Symfony\Component\Yaml\Yaml;
1917

@@ -41,37 +39,26 @@ class SuiteGenerator
4139
*/
4240
private $groupClassGenerator;
4341

44-
/**
45-
* Multidimensional array which represents a custom suite configuration (e.g. certain tests run within a suite etc.)
46-
*
47-
* @var array
48-
*/
49-
private $suiteReferences;
50-
5142
/**
5243
* SuiteGenerator constructor.
53-
*
54-
* @param array $suiteReferences
5544
*/
56-
private function __construct($suiteReferences)
45+
private function __construct()
5746
{
5847
$this->groupClassGenerator = new GroupClassGenerator();
59-
$this->suiteReferences = $suiteReferences;
6048
}
6149

6250
/**
6351
* Singleton method which is used to retrieve the instance of the suite generator.
6452
*
65-
* @param array $suiteReferences
6653
* @return SuiteGenerator
6754
*/
68-
public static function getInstance($suiteReferences = [])
55+
public static function getInstance()
6956
{
7057
if (!self::$SUITE_GENERATOR_INSTANCE) {
7158
// clear any previous configurations before any generation occurs.
7259
self::clearPreviousGroupPreconditions();
7360
self::clearPreviousSessionConfigEntries();
74-
self::$SUITE_GENERATOR_INSTANCE = new SuiteGenerator($suiteReferences);
61+
self::$SUITE_GENERATOR_INSTANCE = new SuiteGenerator();
7562
}
7663

7764
return self::$SUITE_GENERATOR_INSTANCE;
@@ -86,17 +73,23 @@ public static function getInstance($suiteReferences = [])
8673
*/
8774
public function generateAllSuites($testManifest)
8875
{
89-
$suites = SuiteObjectHandler::getInstance()->getAllObjects();
90-
if (get_class($testManifest) == ParallelTestManifest::class) {
91-
/** @var ParallelTestManifest $testManifest */
92-
$suites = $testManifest->getSorter()->getResultingSuites();
93-
} elseif (!empty($this->suiteReferences)) {
94-
$suites = array_intersect_key($suites, $this->suiteReferences);
76+
$suites = array_keys(SuiteObjectHandler::getInstance()->getAllObjects());
77+
if ($testManifest != null) {
78+
$suites = $testManifest->getSuiteConfig();
9579
}
9680

97-
foreach ($suites as $suite) {
98-
// during a parallel config run we must generate only after we have data around how a suite will be split
99-
$this->generateSuiteFromObject($suite);
81+
foreach ($suites as $suiteName => $suiteContent) {
82+
$firstElement = array_values($suiteContent)[0];
83+
84+
// if the first element is a string we know that we simply have an array of tests
85+
if (is_string($firstElement)) {
86+
$this->generateSuiteFromTest($suiteName, $suiteContent);
87+
}
88+
89+
// if our first element is an array we know that we have split the suites
90+
if (is_array($firstElement)) {
91+
$this->generateSplitSuiteFromTest($suiteName, $suiteContent);
92+
}
10093
}
10194
}
10295

@@ -141,47 +134,96 @@ public function getTestsReferencedInSuites()
141134
public function generateSuite($suiteName)
142135
{
143136
/**@var SuiteObject $suite **/
144-
$suite = SuiteObjectHandler::getInstance()->getObject($suiteName);
145-
$this->generateSuiteFromObject($suite);
137+
$this->generateSuiteFromTest($suiteName, []);
146138
}
147139

148140
/**
149-
* Function which takes a suite object and generates all relevant supporting files and classes.
141+
* Function which takes a suite name and a set of test names. The function then generates all relevant supporting
142+
* files and classes for the suite. The function takes an optional argument for suites which are split by a parallel
143+
* run so that any pre/post conditions can be duplicated.
150144
*
151-
* @param SuiteObject $suiteObject
145+
* @param string $suiteName
146+
* @param array $tests
147+
* @param string $originalSuiteName
152148
* @return void
153149
*/
154-
public function generateSuiteFromObject($suiteObject)
150+
private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteName = null)
155151
{
156-
$suiteName = $suiteObject->getName();
157152
$relativePath = TestGenerator::GENERATED_DIR . DIRECTORY_SEPARATOR . $suiteName;
158-
$fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $relativePath;
159-
$groupNamespace = null;
153+
$fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $relativePath . DIRECTORY_SEPARATOR;
160154

161155
DirSetupUtil::createGroupDir($fullPath);
162156

163-
$relevantTests = $suiteObject->getTests();
164-
if (array_key_exists($suiteName, $this->suiteReferences)) {
165-
$testReferences = $this->suiteReferences[$suiteName] ?? [];
166-
$tmpRelevantTests = null;
167-
array_walk($testReferences, function ($value) use (&$tmpRelevantTests, $relevantTests) {
168-
$tmpRelevantTests[$value] = $relevantTests[$value];
169-
});
170-
171-
$relevantTests = $tmpRelevantTests ?? $relevantTests;
157+
$relevantTests = [];
158+
if (!empty($tests)) {
159+
foreach ($tests as $testName) {
160+
$relevantTests[$testName] = TestObjectHandler::getInstance()->getObject($testName);
161+
}
162+
} else {
163+
$relevantTests = SuiteObjectHandler::getInstance()->getObject($suiteName)->getTests();
172164
}
173165

174166
$this->generateRelevantGroupTests($suiteName, $relevantTests);
175-
176-
if ($suiteObject->requiresGroupFile()) {
177-
// if the suite requires a group file, generate it and set the namespace
178-
$groupNamespace = $this->groupClassGenerator->generateGroupClass($suiteObject);
179-
}
167+
$groupNamespace = $this->generateGroupFile($suiteName, $relevantTests, $originalSuiteName);
180168

181169
$this->appendEntriesToConfig($suiteName, $fullPath, $groupNamespace);
182170
print "Suite ${suiteName} generated to ${relativePath}.\n";
183171
}
184172

173+
/**
174+
* Function for generating split groups of tests (following a parallel execution). Takes a paralle suite config
175+
* and generates applicable suites.
176+
*
177+
* @param string $suiteName
178+
* @param array $suiteContent
179+
* @return void
180+
*/
181+
private function generateSplitSuiteFromTest($suiteName, $suiteContent)
182+
{
183+
foreach ($suiteContent as $suiteSplitName => $tests) {
184+
$this->generateSuiteFromTest($suiteSplitName, $tests, $suiteName);
185+
}
186+
}
187+
188+
/**
189+
* Function which takes a suite name, array of tests, and an original suite name. The function takes these args
190+
* and generates a group file which captures suite level preconditions.
191+
*
192+
* @param string $suiteName
193+
* @param array $tests
194+
* @param string $originalSuiteName
195+
* @return null|string
196+
*/
197+
private function generateGroupFile($suiteName, $tests, $originalSuiteName)
198+
{
199+
// if there's an original suite name we know that this test came from a split group.
200+
if ($originalSuiteName) {
201+
// create the new suite object
202+
/** @var SuiteObject $originalSuite */
203+
$originalSuite = SuiteObjectHandler::getInstance()->getObject($originalSuiteName);
204+
$suiteObject = new SuiteObject(
205+
$suiteName,
206+
$tests,
207+
[],
208+
$originalSuite->getHooks()
209+
);
210+
} else {
211+
$suiteObject = SuiteObjectHandler::getInstance()->getObject($suiteName);
212+
// we have to handle the case when there is a custom configuration for an existing suite.
213+
if (count($suiteObject->getTests()) != count($tests)) {
214+
return $this->generateGroupFile($suiteName, $tests, $suiteName);
215+
}
216+
}
217+
218+
if (!$suiteObject->requiresGroupFile()) {
219+
// if we do not require a group file we don't need a namespace
220+
return null;
221+
}
222+
223+
// if the suite requires a group file, generate it and set the namespace
224+
return $this->groupClassGenerator->generateGroupClass($suiteObject);
225+
}
226+
185227
/**
186228
* Function which accepts a suite name and suite path and appends a new group entry to the codeception.yml.dist
187229
* file in order to register the set of tests as a new group. Also appends group object location if required
@@ -255,7 +297,7 @@ private static function clearPreviousSessionConfigEntries()
255297
private function generateRelevantGroupTests($path, $tests)
256298
{
257299
$testGenerator = TestGenerator::getInstance($path, $tests);
258-
$testGenerator->createAllTestFiles('suite');
300+
$testGenerator->createAllTestFiles(null, []);
259301
}
260302

261303
/**

src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\FunctionalTestingFramework\Util\Manifest;
88

9+
use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler;
10+
use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject;
911
use Magento\FunctionalTestingFramework\Test\Objects\TestObject;
1012

1113
abstract class BaseTestManifest
@@ -24,16 +26,25 @@ abstract class BaseTestManifest
2426
*/
2527
protected $relativeDirPath;
2628

29+
/**
30+
* Suite configuration in the format suite name to test name. Overwritten during a custom configuration.
31+
*
32+
* @var array
33+
*/
34+
protected $suiteConfiguration;
35+
2736
/**
2837
* TestManifest constructor.
2938
*
3039
* @param string $path
3140
* @param string $runConfig
41+
* @param array $suiteConfiguration
3242
*/
33-
public function __construct($path, $runConfig)
43+
public function __construct($path, $runConfig, $suiteConfiguration)
3444
{
3545
$this->runTypeConfig = $runConfig;
3646
$this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1);
47+
$this->suiteConfiguration = $suiteConfiguration;
3748
}
3849

3950
/**
@@ -57,9 +68,38 @@ abstract public function addTest($testObject);
5768
/**
5869
* Function which generates the actual manifest(s) once the relevant tests have been added to the array.
5970
*
60-
* @param array $testsReferencedInSuites
61-
* @param int|null $nodes
6271
* @return void
6372
*/
64-
abstract public function generate($testsReferencedInSuites, $nodes = null);
73+
abstract public function generate();
74+
75+
/**
76+
* Getter for the suite configuration.
77+
*
78+
* @return array
79+
*/
80+
public function getSuiteConfig()
81+
{
82+
$suiteToTestNames = [];
83+
84+
if (empty($this->suiteConfiguration)) {
85+
// if there is no configuration passed we can assume the user wants all suites generated as specified.
86+
foreach (SuiteObjectHandler::getInstance()->getAllObjects() as $suite => $suiteObj) {
87+
/** @var SuiteObject $suitObj */
88+
$suiteToTestNames[$suite] = array_keys($suiteObj->getTests());
89+
}
90+
} else {
91+
// we need to loop through the configuration to make sure we capture suites with no specific config
92+
foreach ($this->suiteConfiguration as $suiteName => $test) {
93+
if (empty($test)) {
94+
$suiteToTestNames[$suiteName] =
95+
array_keys(SuiteObjectHandler::getInstance()->getObject($suiteName)->getTests());
96+
continue;
97+
}
98+
99+
$suiteToTestNames[$suiteName] = $test;
100+
}
101+
}
102+
103+
return $suiteToTestNames;
104+
}
65105
}

0 commit comments

Comments
 (0)