Skip to content

Commit 9fb3fdc

Browse files
authored
Merge pull request #132 from magento-commerce/MQE-2669_alex
Seprated a run:failed command to generate:failed and run:failed
2 parents 4db4779 + db97baf commit 9fb3fdc

File tree

5 files changed

+269
-95
lines changed

5 files changed

+269
-95
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ dev/tests/docs/*
2020
dev/tests/_output
2121
dev/tests/functional.suite.yml
2222
/v2/
23+
dev/.credentials.example
24+
dev/tests/.phpunit.result.cache
25+
dev/tests/verification/TestModule/Test/testFile.xml
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace tests\unit\Magento\FunctionalTestFramework\Console;
7+
8+
use Magento\FunctionalTestingFramework\Console\GenerateTestFailedCommand;
9+
use Magento\FunctionalTestingFramework\Test\Objects\TestObject;
10+
use PHPUnit\Framework\MockObject\MockBuilder;
11+
use PHPUnit\Framework\TestCase;
12+
use Magento\FunctionalTestingFramework\Exceptions\FastFailException;
13+
use Magento\FunctionalTestingFramework\Console\GenerateTestsCommand;
14+
use ReflectionClass;
15+
16+
class GenerateTestFailedCommandTest extends BaseGenerateCommandTest
17+
{
18+
public function testSingleTestNoSuite(): void
19+
{
20+
$testFileReturn = [
21+
"tests/functional/tests/MFTF/_generated/default/SingleTestNoSuiteTest.php:SingleTestNoSuiteTest"
22+
];
23+
$expectedConfiguration = '{"tests":["SingleTestNoSuiteTest"],"suites":null}';
24+
25+
// Create a stub for the SomeClass class.
26+
$stub = $this->getMockBuilder(GenerateTestFailedCommand::class)
27+
->onlyMethods(["readFailedTestFile", "writeFailedTestToFile"])
28+
->getMock();
29+
// Configure the stub.
30+
$stub
31+
->method('readFailedTestFile')
32+
->willReturn($testFileReturn);
33+
$stub
34+
->method('writeFailedTestToFile')
35+
->willReturn(null);
36+
37+
// Run the real code
38+
$configuration = $stub->getFailedTestList("", "");
39+
$this->assertEquals($expectedConfiguration, $configuration);
40+
}
41+
}

src/Magento/FunctionalTestingFramework/Console/CommandList.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public function __construct(array $commands = [])
3636
'generate:tests' => new GenerateTestsCommand(),
3737
'generate:urn-catalog' => new GenerateDevUrnCommand(),
3838
'reset' => new CleanProjectCommand(),
39+
'generate:failed' => new GenerateTestFailedCommand(),
3940
'run:failed' => new RunTestFailedCommand(),
4041
'run:group' => new RunTestGroupCommand(),
4142
'run:manifest' => new RunManifestCommand(),
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types = 1);
7+
8+
namespace Magento\FunctionalTestingFramework\Console;
9+
10+
use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig;
11+
use Magento\FunctionalTestingFramework\Util\Path\FilePathFormatter;
12+
use Symfony\Component\Console\Input\ArrayInput;
13+
use Symfony\Component\Console\Input\InputInterface;
14+
use Symfony\Component\Console\Output\OutputInterface;
15+
use Symfony\Component\Process\Process;
16+
use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
17+
use Symfony\Component\Console\Input\InputOption;
18+
19+
class GenerateTestFailedCommand extends BaseGenerateCommand
20+
{
21+
/**
22+
* Default Test group to signify not in suite
23+
*/
24+
const DEFAULT_TEST_GROUP = 'default';
25+
26+
/**
27+
* Configures the current command.
28+
*
29+
* @return void
30+
*/
31+
protected function configure()
32+
{
33+
$this->setName('generate:failed')
34+
->setDescription('Generate a set of tests failed');
35+
36+
parent::configure();
37+
}
38+
39+
/**
40+
* Executes the current command.
41+
*
42+
* @param InputInterface $input
43+
* @param OutputInterface $output
44+
* @return integer
45+
* @throws \Exception
46+
*
47+
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
48+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
49+
*/
50+
protected function execute(InputInterface $input, OutputInterface $output): int
51+
{
52+
$force = $input->getOption('force');
53+
$debug = $input->getOption('debug') ?? MftfApplicationConfig::LEVEL_DEVELOPER; // for backward compatibility
54+
$allowSkipped = $input->getOption('allow-skipped');
55+
$verbose = $output->isVerbose();
56+
57+
// Create Mftf Configuration
58+
MftfApplicationConfig::create(
59+
$force,
60+
MftfApplicationConfig::EXECUTION_PHASE,
61+
$verbose,
62+
$debug,
63+
$allowSkipped
64+
);
65+
66+
$testsFailedFile = $this->getTestsOutputDir() . self::FAILED_FILE;
67+
$testsReRunFile = $this->getTestsOutputDir() . "rerun_tests";
68+
$testConfiguration = $this->getFailedTestList($testsFailedFile, $testsReRunFile);
69+
70+
if ($testConfiguration === null) {
71+
// No failed tests found, no tests generated
72+
$this->removeGeneratedDirectory($output, $verbose);
73+
return 0;
74+
}
75+
76+
$command = $this->getApplication()->find('generate:tests');
77+
$args = [
78+
'--tests' => $testConfiguration,
79+
'--force' => $force,
80+
'--remove' => true,
81+
'--debug' => $debug,
82+
'--allow-skipped' => $allowSkipped,
83+
'-v' => $verbose
84+
];
85+
$command->run(new ArrayInput($args), $output);
86+
$output->writeln("Test Failed Generated, now run:failed command");
87+
return 0;
88+
}
89+
90+
/**
91+
* Returns a json string of tests that failed on the last run
92+
*
93+
* @return string
94+
*/
95+
public function getFailedTestList($testsFailedFile, $testsReRunFile)
96+
{
97+
$failedTestDetails = ['tests' => [], 'suites' => []];
98+
99+
$testList = $this->readFailedTestFile($testsFailedFile);
100+
101+
if (!empty($testList)) {
102+
foreach ($testList as $test) {
103+
if (!empty($test)) {
104+
$this->writeFailedTestToFile($test, $testsReRunFile);
105+
$testInfo = explode(DIRECTORY_SEPARATOR, $test);
106+
$testName = explode(":", $testInfo[count($testInfo) - 1])[1];
107+
$suiteName = $testInfo[count($testInfo) - 2];
108+
109+
if ($suiteName === self::DEFAULT_TEST_GROUP) {
110+
array_push($failedTestDetails['tests'], $testName);
111+
} else {
112+
$suiteName = $this->sanitizeSuiteName($suiteName);
113+
$failedTestDetails['suites'] = array_merge_recursive(
114+
$failedTestDetails['suites'],
115+
[$suiteName => [$testName]]
116+
);
117+
}
118+
}
119+
}
120+
}
121+
if (empty($failedTestDetails['tests']) & empty($failedTestDetails['suites'])) {
122+
return null;
123+
}
124+
if (empty($failedTestDetails['tests'])) {
125+
$failedTestDetails['tests'] = null;
126+
}
127+
if (empty($failedTestDetails['suites'])) {
128+
$failedTestDetails['suites'] = null;
129+
}
130+
$testConfigurationJson = json_encode($failedTestDetails);
131+
return $testConfigurationJson;
132+
}
133+
134+
/**
135+
* Trim potential suite_parallel_0_G to suite_parallel
136+
*
137+
* @param string $suiteName
138+
* @return string
139+
*/
140+
private function sanitizeSuiteName($suiteName)
141+
{
142+
$suiteNameArray = explode("_", $suiteName);
143+
if (array_pop($suiteNameArray) === 'G') {
144+
if (is_numeric(array_pop($suiteNameArray))) {
145+
$suiteName = implode("_", $suiteNameArray);
146+
}
147+
}
148+
return $suiteName;
149+
}
150+
151+
/**
152+
* Returns an array of tests read from the failed test file in _output
153+
*
154+
* @param string $filePath
155+
* @return array|boolean
156+
*/
157+
public function readFailedTestFile($filePath)
158+
{
159+
if (realpath($filePath)) {
160+
return file($filePath, FILE_IGNORE_NEW_LINES);
161+
}
162+
return "";
163+
}
164+
165+
/**
166+
* Writes the test name to a file if it does not already exist
167+
*
168+
* @param string $test
169+
* @param string $filePath
170+
* @return void
171+
*/
172+
public function writeFailedTestToFile($test, $filePath)
173+
{
174+
if (file_exists($filePath)) {
175+
if (strpos(file_get_contents($filePath), $test) === false) {
176+
file_put_contents($filePath, "\n" . $test, FILE_APPEND);
177+
}
178+
} else {
179+
file_put_contents($filePath, $test . "\n");
180+
}
181+
}
182+
}

0 commit comments

Comments
 (0)