66
77namespace Magento \FunctionalTestingFramework \Suite ;
88
9- use Magento \Framework \Phrase ;
10- use Magento \Framework \Validator \Exception ;
9+ use Magento \FunctionalTestingFramework \Exceptions \TestReferenceException ;
1110use Magento \FunctionalTestingFramework \Suite \Generators \GroupClassGenerator ;
1211use Magento \FunctionalTestingFramework \Suite \Handlers \SuiteObjectHandler ;
1312use Magento \FunctionalTestingFramework \Suite \Objects \SuiteObject ;
13+ use Magento \FunctionalTestingFramework \Test \Handlers \TestObjectHandler ;
1414use Magento \FunctionalTestingFramework \Util \Filesystem \DirSetupUtil ;
1515use Magento \FunctionalTestingFramework \Util \Manifest \BaseTestManifest ;
16- use Magento \FunctionalTestingFramework \Util \Manifest \ParallelTestManifest ;
1716use Magento \FunctionalTestingFramework \Util \TestGenerator ;
1817use Symfony \Component \Yaml \Yaml ;
1918
@@ -75,15 +74,23 @@ public static function getInstance()
7574 */
7675 public function generateAllSuites ($ testManifest )
7776 {
78- $ suites = SuiteObjectHandler::getInstance ()->getAllObjects ();
79- if (get_class ($ testManifest ) == ParallelTestManifest::class) {
80- /** @var ParallelTestManifest $testManifest */
81- $ suites = $ testManifest ->getSorter ()->getResultingSuites ();
77+ $ suites = array_keys (SuiteObjectHandler::getInstance ()->getAllObjects ());
78+ if ($ testManifest != null ) {
79+ $ suites = $ testManifest ->getSuiteConfig ();
8280 }
8381
84- foreach ($ suites as $ suite ) {
85- // during a parallel config run we must generate only after we have data around how a suite will be split
86- $ this ->generateSuiteFromObject ($ suite );
82+ foreach ($ suites as $ suiteName => $ suiteContent ) {
83+ $ firstElement = array_values ($ suiteContent )[0 ];
84+
85+ // if the first element is a string we know that we simply have an array of tests
86+ if (is_string ($ firstElement )) {
87+ $ this ->generateSuiteFromTest ($ suiteName , $ suiteContent );
88+ }
89+
90+ // if our first element is an array we know that we have split the suites
91+ if (is_array ($ firstElement )) {
92+ $ this ->generateSplitSuiteFromTest ($ suiteName , $ suiteContent );
93+ }
8794 }
8895 }
8996
@@ -96,11 +103,22 @@ public function getTestsReferencedInSuites()
96103 {
97104 $ testsReferencedInSuites = [];
98105 $ suites = SuiteObjectHandler::getInstance ()->getAllObjects ();
106+
107+ // see if we have a specific suite configuration.
108+ if (!empty ($ this ->suiteReferences )) {
109+ $ suites = array_intersect_key ($ suites , $ this ->suiteReferences );
110+ }
111+
99112 foreach ($ suites as $ suite ) {
100113 /** @var SuiteObject $suite */
101114 $ test_keys = array_keys ($ suite ->getTests ());
102- $ testToSuiteName = array_fill_keys ($ test_keys , [$ suite ->getName ()]);
103115
116+ // see if we need to filter which tests we'll be generating.
117+ if (array_key_exists ($ suite ->getName (), $ this ->suiteReferences )) {
118+ $ test_keys = $ this ->suiteReferences [$ suite ->getName ()] ?? $ test_keys ;
119+ }
120+
121+ $ testToSuiteName = array_fill_keys ($ test_keys , [$ suite ->getName ()]);
104122 $ testsReferencedInSuites = array_merge_recursive ($ testsReferencedInSuites , $ testToSuiteName );
105123 }
106124
@@ -117,35 +135,125 @@ public function getTestsReferencedInSuites()
117135 public function generateSuite ($ suiteName )
118136 {
119137 /**@var SuiteObject $suite **/
120- $ suite = SuiteObjectHandler::getInstance ()->getObject ($ suiteName );
121- $ this ->generateSuiteFromObject ($ suite );
138+ $ this ->generateSuiteFromTest ($ suiteName , []);
122139 }
123140
124141 /**
125- * Function which takes a suite object and generates all relevant supporting files and classes.
142+ * Function which takes a suite name and a set of test names. The function then generates all relevant supporting
143+ * files and classes for the suite. The function takes an optional argument for suites which are split by a parallel
144+ * run so that any pre/post conditions can be duplicated.
126145 *
127- * @param SuiteObject $suiteObject
146+ * @param string $suiteName
147+ * @param array $tests
148+ * @param string $originalSuiteName
128149 * @return void
129150 */
130- public function generateSuiteFromObject ( $ suiteObject )
151+ private function generateSuiteFromTest ( $ suiteName , $ tests = [], $ originalSuiteName = null )
131152 {
132- $ suiteName = $ suiteObject ->getName ();
133153 $ relativePath = TestGenerator::GENERATED_DIR . DIRECTORY_SEPARATOR . $ suiteName ;
134- $ fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $ relativePath ;
135- $ groupNamespace = null ;
154+ $ fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $ relativePath . DIRECTORY_SEPARATOR ;
136155
137156 DirSetupUtil::createGroupDir ($ fullPath );
138- $ this ->generateRelevantGroupTests ($ suiteName , $ suiteObject ->getTests ());
139157
140- if ($ suiteObject ->requiresGroupFile ()) {
141- // if the suite requires a group file, generate it and set the namespace
142- $ groupNamespace = $ this ->groupClassGenerator ->generateGroupClass ($ suiteObject );
158+ $ relevantTests = [];
159+ if (!empty ($ tests )) {
160+ $ this ->validateTestsReferencedInSuite ($ suiteName , $ tests , $ originalSuiteName );
161+ foreach ($ tests as $ testName ) {
162+ $ relevantTests [$ testName ] = TestObjectHandler::getInstance ()->getObject ($ testName );
163+ }
164+ } else {
165+ $ relevantTests = SuiteObjectHandler::getInstance ()->getObject ($ suiteName )->getTests ();
143166 }
144167
168+ $ this ->generateRelevantGroupTests ($ suiteName , $ relevantTests );
169+ $ groupNamespace = $ this ->generateGroupFile ($ suiteName , $ relevantTests , $ originalSuiteName );
170+
145171 $ this ->appendEntriesToConfig ($ suiteName , $ fullPath , $ groupNamespace );
146172 print "Suite $ {suiteName} generated to $ {relativePath}. \n" ;
147173 }
148174
175+ /**
176+ * Function which validates tests passed in as custom configuration against the configuration defined by the user to
177+ * prevent possible invalid test configurations from executing.
178+ *
179+ * @param string $suiteName
180+ * @param array $testsReferenced
181+ * @param string $originalSuiteName
182+ * @return void
183+ * @throws TestReferenceException
184+ */
185+ private function validateTestsReferencedInSuite ($ suiteName , $ testsReferenced , $ originalSuiteName )
186+ {
187+ $ suiteRef = $ originalSuiteName ?? $ suiteName ;
188+ $ possibleTestRef = SuiteObjectHandler::getInstance ()->getObject ($ suiteRef )->getTests ();
189+ $ invalidTestRef = null ;
190+ $ errorMsg = "Cannot reference tests not declared as part of {$ suiteRef }: \n " ;
191+
192+ array_walk ($ testsReferenced , function ($ value ) use (&$ invalidTestRef , $ possibleTestRef , &$ errorMsg ) {
193+ if (!array_key_exists ($ value , $ possibleTestRef )) {
194+ $ invalidTestRef .= "\t{$ value }\n" ;
195+ }
196+ });
197+
198+ if ($ invalidTestRef != null ) {
199+ throw new TestReferenceException ($ errorMsg . $ invalidTestRef );
200+ }
201+ }
202+
203+ /**
204+ * Function for generating split groups of tests (following a parallel execution). Takes a paralle suite config
205+ * and generates applicable suites.
206+ *
207+ * @param string $suiteName
208+ * @param array $suiteContent
209+ * @return void
210+ */
211+ private function generateSplitSuiteFromTest ($ suiteName , $ suiteContent )
212+ {
213+ foreach ($ suiteContent as $ suiteSplitName => $ tests ) {
214+ $ this ->generateSuiteFromTest ($ suiteSplitName , $ tests , $ suiteName );
215+ }
216+ }
217+
218+ /**
219+ * Function which takes a suite name, array of tests, and an original suite name. The function takes these args
220+ * and generates a group file which captures suite level preconditions.
221+ *
222+ * @param string $suiteName
223+ * @param array $tests
224+ * @param string $originalSuiteName
225+ * @return null|string
226+ */
227+ private function generateGroupFile ($ suiteName , $ tests , $ originalSuiteName )
228+ {
229+ // if there's an original suite name we know that this test came from a split group.
230+ if ($ originalSuiteName ) {
231+ // create the new suite object
232+ /** @var SuiteObject $originalSuite */
233+ $ originalSuite = SuiteObjectHandler::getInstance ()->getObject ($ originalSuiteName );
234+ $ suiteObject = new SuiteObject (
235+ $ suiteName ,
236+ $ tests ,
237+ [],
238+ $ originalSuite ->getHooks ()
239+ );
240+ } else {
241+ $ suiteObject = SuiteObjectHandler::getInstance ()->getObject ($ suiteName );
242+ // we have to handle the case when there is a custom configuration for an existing suite.
243+ if (count ($ suiteObject ->getTests ()) != count ($ tests )) {
244+ return $ this ->generateGroupFile ($ suiteName , $ tests , $ suiteName );
245+ }
246+ }
247+
248+ if (!$ suiteObject ->requiresGroupFile ()) {
249+ // if we do not require a group file we don't need a namespace
250+ return null ;
251+ }
252+
253+ // if the suite requires a group file, generate it and set the namespace
254+ return $ this ->groupClassGenerator ->generateGroupClass ($ suiteObject );
255+ }
256+
149257 /**
150258 * Function which accepts a suite name and suite path and appends a new group entry to the codeception.yml.dist
151259 * file in order to register the set of tests as a new group. Also appends group object location if required
@@ -219,7 +327,7 @@ private static function clearPreviousSessionConfigEntries()
219327 private function generateRelevantGroupTests ($ path , $ tests )
220328 {
221329 $ testGenerator = TestGenerator::getInstance ($ path , $ tests );
222- $ testGenerator ->createAllTestFiles (' suite ' );
330+ $ testGenerator ->createAllTestFiles (null , [] );
223331 }
224332
225333 /**
0 commit comments