Skip to content

MQE-1703: Implicit Suite Generation for Tests #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 5, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil;
use Magento\FunctionalTestingFramework\Util\TestGenerator;
use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig;
use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler;

class BaseGenerateCommand extends Command
{
Expand Down Expand Up @@ -67,4 +68,40 @@ protected function removeGeneratedDirectory(OutputInterface $output, bool $verbo
}
}
}

/**
* Returns an array of test configuration to be used as an argument for generation of tests
* @param array $tests
* @return false|string
* @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException
*/

protected function getTestAndSuiteConfiguration(array $tests)
{
$testConfiguration['tests'] = null;
$testConfiguration['suites'] = null;
$testsReferencedInSuites = SuiteObjectHandler::getInstance()->getAllTestReferences();
$resolvedTests = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor but rename this to $suiteToTestPair, I thought at first this would hold either type of test but it only holds suite:test pairs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.


foreach($tests as $test) {
if (array_key_exists($test, $testsReferencedInSuites)) {
$suites = $testsReferencedInSuites[$test];
$resolvedTests = array_merge(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this array_merge/map combo to something a little more simple?

foreach ($suites as $suite) {
    $resolvedTests[] = "$suite:$test";
}

array_map/merge are both recursive loops at the language level (at least I know map is) so this alternative is either equal or actually faster (easier to read in either case)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was trying to avoid nested loops as I didn't find many implementations of it in code, thought it was a style thing. Changed it.

$resolvedTests,
array_map(function ($value) use ($test) {
return $value . ':' . $test;
}, $suites)
);
}
// configuration for tests
else $testConfiguration['tests'][] = $test;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to use multiline else formatting:

            } else {
                $testConfiguration['tests'][] = $test;
            }

}
// configuration for suites
foreach ($resolvedTests as $test) {
list($suite, $test) = explode(":", $test);
$testConfiguration['suites'][$suite][] = $test;
}
$testConfigurationJson = json_encode($testConfiguration);
return $testConfigurationJson;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,18 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$tests = $input->getArgument('name');
$config = $input->getOption('config');
$json = $input->getOption('tests');
$json = $input->getOption('tests'); // for backward compatibility
$force = $input->getOption('force');
$time = $input->getOption('time') * 60 * 1000; // convert from minutes to milliseconds
$debug = $input->getOption('debug') ?? MftfApplicationConfig::LEVEL_DEVELOPER; // for backward compatibility
$remove = $input->getOption('remove');
$verbose = $output->isVerbose();
$allowSkipped = $input->getOption('allowSkipped');

if (!empty($tests)) {
$json = $this->getTestAndSuiteConfiguration($tests);
}

if ($json !== null && !json_decode($json)) {
// stop execution if we have failed to properly parse any json passed in by the user
throw new TestFrameworkException("JSON could not be parsed: " . json_last_error_msg());
Expand Down Expand Up @@ -100,9 +104,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$testManifest->createTestGroups($time);
}

if (empty($tests)) {
SuiteGenerator::getInstance()->generateAllSuites($testManifest);
}
SuiteGenerator::getInstance()->generateAllSuites($testManifest);

$testManifest->generate();

Expand Down
46 changes: 38 additions & 8 deletions src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,28 +63,35 @@ protected function execute(InputInterface $input, OutputInterface $output): int
);
}

$testConfiguration = $this->getTestAndSuiteConfiguration($tests);

if (!$skipGeneration) {
$command = $this->getApplication()->find('generate:tests');
$args = [
'--tests' => json_encode([
'tests' => $tests,
'suites' => null
]),
'--tests' => $testConfiguration,
'--force' => $force,
'--remove' => $remove,
'--debug' => $debug,
'--allowSkipped' => $allowSkipped
];
$command->run(new ArrayInput($args), $output);
}
// tests with resolved suite references
$resolvedTests = $this->resolveSuiteReferences($testConfiguration);

$returnCode = 0;
$codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional ';
$testsDirectory = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . TestGenerator::GENERATED_DIR . DIRECTORY_SEPARATOR;
$returnCode = 0;
//execute only tests specified as arguments in run command
foreach ($tests as $test) {
$testGroup = TestGenerator::DEFAULT_DIR . DIRECTORY_SEPARATOR;
$testName = $test . 'Cest.php';
foreach ($resolvedTests as $test) {
//set directory as suite name for tests in suite, if not set to "default"
if (strpos($test, ':')) {
list($testGroup, $testName) = explode(":", $test);
} else {
list($testGroup, $testName) = [TestGenerator::DEFAULT_DIR, $test];
}
$testGroup = $testGroup . DIRECTORY_SEPARATOR;
$testName = $testName . 'Cest.php';
if (!realpath($testsDirectory . $testGroup . $testName)) {
throw new TestFrameworkException(
$testName . " is not available under " . $testsDirectory . $testGroup
Expand All @@ -104,4 +111,27 @@ function ($type, $buffer) use ($output) {
}
return $returnCode;
}

/** Get an array of tests with resolved suite references from $testConfiguration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top of docblock needs empty line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

* eg: if test is referenced in a suite, it'll be stored in format suite:test
* @param string $testConfigurationJson
* @return array
*/
private function resolveSuiteReferences($testConfigurationJson)
{
$testConfiguration = json_decode($testConfigurationJson, true);
$testsArray = $testConfiguration['tests'] ?? [];
$suitesArray = $testConfiguration['suites'] ?? [];
$testArrayBuilder = [];

foreach ($suitesArray as $suite => $tests) {
$testArrayBuilder = array_merge(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See commend in BaseGenerateCommand, this can probably be simplified into one foreach

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

$testArrayBuilder,
array_map(function ($test) use ($suite) {
return $suite . ':' . $test;
}, $tests)
);
}
return array_merge($testArrayBuilder, $testsArray);
}
}