66
77namespace Magento \FunctionalTestingFramework \Suite ;
88
9- use Magento \Framework \Phrase ;
10- use Magento \Framework \Validator \Exception ;
119use Magento \FunctionalTestingFramework \Suite \Generators \GroupClassGenerator ;
1210use Magento \FunctionalTestingFramework \Suite \Handlers \SuiteObjectHandler ;
1311use Magento \FunctionalTestingFramework \Suite \Objects \SuiteObject ;
12+ use Magento \FunctionalTestingFramework \Test \Handlers \TestObjectHandler ;
1413use Magento \FunctionalTestingFramework \Util \Filesystem \DirSetupUtil ;
1514use Magento \FunctionalTestingFramework \Util \Manifest \BaseTestManifest ;
16- use Magento \FunctionalTestingFramework \Util \Manifest \ParallelTestManifest ;
1715use Magento \FunctionalTestingFramework \Util \TestGenerator ;
1816use 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 /**
0 commit comments