6
6
7
7
namespace Magento \FunctionalTestingFramework \Suite ;
8
8
9
- use Magento \Framework \Phrase ;
10
- use Magento \Framework \Validator \Exception ;
9
+ use Magento \FunctionalTestingFramework \Exceptions \TestReferenceException ;
11
10
use Magento \FunctionalTestingFramework \Suite \Generators \GroupClassGenerator ;
12
11
use Magento \FunctionalTestingFramework \Suite \Handlers \SuiteObjectHandler ;
13
12
use Magento \FunctionalTestingFramework \Suite \Objects \SuiteObject ;
13
+ use Magento \FunctionalTestingFramework \Test \Handlers \TestObjectHandler ;
14
14
use Magento \FunctionalTestingFramework \Util \Filesystem \DirSetupUtil ;
15
15
use Magento \FunctionalTestingFramework \Util \Manifest \BaseTestManifest ;
16
- use Magento \FunctionalTestingFramework \Util \Manifest \ParallelTestManifest ;
17
16
use Magento \FunctionalTestingFramework \Util \TestGenerator ;
18
17
use Symfony \Component \Yaml \Yaml ;
19
18
@@ -75,15 +74,23 @@ public static function getInstance()
75
74
*/
76
75
public function generateAllSuites ($ testManifest )
77
76
{
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 ();
82
80
}
83
81
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
+ }
87
94
}
88
95
}
89
96
@@ -96,11 +103,22 @@ public function getTestsReferencedInSuites()
96
103
{
97
104
$ testsReferencedInSuites = [];
98
105
$ 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
+
99
112
foreach ($ suites as $ suite ) {
100
113
/** @var SuiteObject $suite */
101
114
$ test_keys = array_keys ($ suite ->getTests ());
102
- $ testToSuiteName = array_fill_keys ($ test_keys , [$ suite ->getName ()]);
103
115
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 ()]);
104
122
$ testsReferencedInSuites = array_merge_recursive ($ testsReferencedInSuites , $ testToSuiteName );
105
123
}
106
124
@@ -117,35 +135,125 @@ public function getTestsReferencedInSuites()
117
135
public function generateSuite ($ suiteName )
118
136
{
119
137
/**@var SuiteObject $suite **/
120
- $ suite = SuiteObjectHandler::getInstance ()->getObject ($ suiteName );
121
- $ this ->generateSuiteFromObject ($ suite );
138
+ $ this ->generateSuiteFromTest ($ suiteName , []);
122
139
}
123
140
124
141
/**
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.
126
145
*
127
- * @param SuiteObject $suiteObject
146
+ * @param string $suiteName
147
+ * @param array $tests
148
+ * @param string $originalSuiteName
128
149
* @return void
129
150
*/
130
- public function generateSuiteFromObject ( $ suiteObject )
151
+ private function generateSuiteFromTest ( $ suiteName , $ tests = [], $ originalSuiteName = null )
131
152
{
132
- $ suiteName = $ suiteObject ->getName ();
133
153
$ 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 ;
136
155
137
156
DirSetupUtil::createGroupDir ($ fullPath );
138
- $ this ->generateRelevantGroupTests ($ suiteName , $ suiteObject ->getTests ());
139
157
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 ();
143
166
}
144
167
168
+ $ this ->generateRelevantGroupTests ($ suiteName , $ relevantTests );
169
+ $ groupNamespace = $ this ->generateGroupFile ($ suiteName , $ relevantTests , $ originalSuiteName );
170
+
145
171
$ this ->appendEntriesToConfig ($ suiteName , $ fullPath , $ groupNamespace );
146
172
print "Suite $ {suiteName} generated to $ {relativePath}. \n" ;
147
173
}
148
174
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
+
149
257
/**
150
258
* Function which accepts a suite name and suite path and appends a new group entry to the codeception.yml.dist
151
259
* 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()
219
327
private function generateRelevantGroupTests ($ path , $ tests )
220
328
{
221
329
$ testGenerator = TestGenerator::getInstance ($ path , $ tests );
222
- $ testGenerator ->createAllTestFiles (' suite ' );
330
+ $ testGenerator ->createAllTestFiles (null , [] );
223
331
}
224
332
225
333
/**
0 commit comments