diff --git a/src/Magento/FunctionalTestingFramework/Console/CommandList.php b/src/Magento/FunctionalTestingFramework/Console/CommandList.php index 362b29296..ce971146e 100644 --- a/src/Magento/FunctionalTestingFramework/Console/CommandList.php +++ b/src/Magento/FunctionalTestingFramework/Console/CommandList.php @@ -35,6 +35,7 @@ public function __construct(array $commands = []) 'generate:tests' => new GenerateTestsCommand(), 'run:test' => new RunTestCommand(), 'run:group' => new RunTestGroupCommand(), + 'run:failed' => new RunTestFailedCommand(), 'setup:env' => new SetupEnvCommand(), 'upgrade:tests' => new UpgradeTestsCommand(), ] + $commands; diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php new file mode 100644 index 000000000..63fa4261e --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestFailedCommand.php @@ -0,0 +1,196 @@ +setName('run:failed') + ->setDescription('Execute a set of tests referenced via failed file'); + + parent::configure(); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer|null|void + * @throws \Exception + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Create Mftf Configuration + MftfApplicationConfig::create( + false, + MftfApplicationConfig::GENERATION_PHASE, + false, + false + ); + + $testConfiguration = $this->getFailedTestList(); + + if ($testConfiguration === null) { + return null; + } + + $command = $this->getApplication()->find('generate:tests'); + $args = ['--tests' => $testConfiguration, '--remove' => true]; + + $command->run(new ArrayInput($args), $output); + + $testManifestList = $this->readTestManifestFile(); + + foreach ($testManifestList as $testCommand) { + $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional '; + $codeceptionCommand .= $testCommand; + + $process = new Process($codeceptionCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->setIdleTimeout(600); + $process->setTimeout(0); + $process->run( + function ($type, $buffer) use ($output) { + $output->write($buffer); + } + ); + if (file_exists(self::TESTS_FAILED_FILE)) { + $this->failedList = array_merge( + $this->failedList, + $this->readFailedTestFile(self::TESTS_FAILED_FILE) + ); + } + } + foreach ($this->failedList as $test) { + $this->writeFailedTestToFile($test, self::TESTS_FAILED_FILE); + } + } + + /** + * Returns a json string of tests that failed on the last run + * + * @return string + */ + private function getFailedTestList() + { + $failedTestDetails = ['tests' => [], 'suites' => []]; + + if (realpath(self::TESTS_FAILED_FILE)) { + $testList = $this->readFailedTestFile(self::TESTS_FAILED_FILE); + + foreach ($testList as $test) { + if (!empty($test)) { + $this->writeFailedTestToFile($test, self::TESTS_RERUN_FILE); + $testInfo = explode(DIRECTORY_SEPARATOR, $test); + $testName = explode(":", $testInfo[count($testInfo) - 1])[1]; + $suiteName = $testInfo[count($testInfo) - 2]; + + if ($suiteName == self::DEFAULT_TEST_GROUP) { + array_push($failedTestDetails['tests'], $testName); + } else { + $failedTestDetails['suites'] = array_merge_recursive( + $failedTestDetails['suites'], + [$suiteName => [$testName]] + ); + } + } + } + } + if (empty($failedTestDetails['tests']) & empty($failedTestDetails['suites'])) { + return null; + } + if (empty($failedTestDetails['tests'])) { + $failedTestDetails['tests'] = null; + } + if (empty($failedTestDetails['suites'])) { + $failedTestDetails['suites'] = null; + } + $testConfigurationJson = json_encode($failedTestDetails); + return $testConfigurationJson; + } + + /** + * Returns an array of run commands read from the manifest file created post generation + * + * @return array|boolean + */ + private function readTestManifestFile() + { + return file(self::TESTS_MANIFEST_FILE, FILE_IGNORE_NEW_LINES); + } + + /** + * Returns an array of tests read from the failed test file in _output + * + * @param string $filePath + * @return array|boolean + */ + private function readFailedTestFile($filePath) + { + return file($filePath, FILE_IGNORE_NEW_LINES); + } + + /** + * Writes the test name to a file if it does not already exist + * + * @param string $test + * @return void + */ + private function writeFailedTestToFile($test, $filePath) + { + if (file_exists($filePath)) { + if (strpos(file_get_contents($filePath), $test) === false) { + file_put_contents($filePath, "\n" . $test, FILE_APPEND); + } + } else { + file_put_contents($filePath, $test . "\n"); + } + } +}