Skip to content

Commit 387cef3

Browse files
committed
MQE-3251: Release MFTF 2.7.3 to allow filters functionality for Magento 2.3.x
1 parent db4218a commit 387cef3

File tree

4 files changed

+609
-25
lines changed

4 files changed

+609
-25
lines changed

src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,20 @@
1515
use Symfony\Component\Console\Exception\InvalidArgumentException;
1616
use Symfony\Component\Console\Input\InputArgument;
1717
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Input\InputOption;
1819
use Symfony\Component\Console\Output\OutputInterface;
1920
use Exception;
21+
use Symfony\Component\Console\Style\SymfonyStyle;
2022

2123
class StaticChecksCommand extends Command
2224
{
25+
/**
26+
* Associative array containing static ruleset properties.
27+
*
28+
* @var array
29+
*/
30+
private $ruleSet;
31+
2332
/**
2433
* Pool of all existing static check objects
2534
*
@@ -34,6 +43,13 @@ class StaticChecksCommand extends Command
3443
*/
3544
private $staticCheckObjects;
3645

46+
/**
47+
* Console output style
48+
*
49+
* @var SymfonyStyle
50+
*/
51+
protected $ioStyle;
52+
3753
/**
3854
* Configures the current command.
3955
*
@@ -44,14 +60,20 @@ protected function configure()
4460
$list = new StaticChecksList();
4561
$this->allStaticCheckObjects = $list->getStaticChecks();
4662
$staticCheckNames = implode(', ', array_keys($this->allStaticCheckObjects));
47-
$description = "This command will run all static checks on xml test materials. "
48-
. "Available static check scripts are:\n{$staticCheckNames}";
63+
$description = 'This command will run all static checks on xml test materials. '
64+
. 'Available static check scripts are:' . PHP_EOL . $staticCheckNames;
4965
$this->setName('static-checks')
5066
->setDescription($description)
5167
->addArgument(
5268
'names',
5369
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
5470
'name(s) of specific static check script(s) to run'
71+
)->addOption(
72+
'path',
73+
'p',
74+
InputOption::VALUE_OPTIONAL,
75+
'Path to a MFTF test module to run "deprecatedEntityUsage" static check script. ' . PHP_EOL
76+
. 'Option is ignored by other static check scripts.' . PHP_EOL
5577
);
5678
}
5779

@@ -65,32 +87,41 @@ protected function configure()
6587
*/
6688
protected function execute(InputInterface $input, OutputInterface $output)
6789
{
90+
$this->ioStyle = new SymfonyStyle($input, $output);
6891
try {
69-
$this->validateInputArguments($input, $output);
92+
$this->validateInput($input);
7093
} catch (InvalidArgumentException $e) {
7194
LoggingUtil::getInstance()->getLogger(StaticChecksCommand::class)->error($e->getMessage());
72-
$output->writeln($e->getMessage() . " Please fix input arguments and rerun.");
95+
$this->ioStyle->error($e->getMessage() . ' Please fix input argument(s) or option(s) and rerun.');
7396
return 1;
7497
}
7598

99+
$cmdFailed = false;
76100
$errors = [];
77101
foreach ($this->staticCheckObjects as $name => $staticCheck) {
78102
LoggingUtil::getInstance()->getLogger(get_class($staticCheck))->info(
79-
"\nRunning static check script for: " . $name
80-
);
81-
$output->writeln(
82-
"\nRunning static check script for: " . $name
103+
'Running static check script for: ' . $name . PHP_EOL
83104
);
84105

85-
$staticCheck->execute($input);
106+
$this->ioStyle->text(PHP_EOL . 'Running static check script for: ' . $name . PHP_EOL);
107+
$start = microtime(true);
108+
try {
109+
$staticCheck->execute($input);
110+
} catch (Exception $e) {
111+
$cmdFailed = true;
112+
LoggingUtil::getInstance()->getLogger(get_class($staticCheck))->error($e->getMessage() . PHP_EOL);
113+
$this->ioStyle->error($e->getMessage());
114+
}
115+
$end = microtime(true);
116+
$errors += $staticCheck->getErrors();
86117

87118
$staticOutput = $staticCheck->getOutput();
88119
LoggingUtil::getInstance()->getLogger(get_class($staticCheck))->info($staticOutput);
89-
$output->writeln($staticOutput);
90-
$errors += $staticCheck->getErrors();
91-
}
120+
$this->ioStyle->text($staticOutput);
92121

93-
if (empty($errors)) {
122+
$this->ioStyle->text('Total execution time is ' . (string)($end - $start) . ' seconds.' . PHP_EOL);
123+
}
124+
if (!$cmdFailed && empty($errors)) {
94125
return 0;
95126
} else {
96127
return 1;
@@ -104,30 +135,63 @@ protected function execute(InputInterface $input, OutputInterface $output)
104135
* @return void
105136
* @throws InvalidArgumentException
106137
*/
107-
private function validateInputArguments(InputInterface $input)
138+
private function validateInput(InputInterface $input)
108139
{
109140
$this->staticCheckObjects = [];
110141
$requiredChecksNames = $input->getArgument('names');
111-
$invalidCheckNames = [];
112-
// Found user required static check script(s) to run,
113-
// If no static check name is supplied, run all static check scripts
142+
// Build list of static check names to run.
143+
if (empty($requiredChecksNames)) {
144+
$this->parseRulesetJson();
145+
$requiredChecksNames = $this->ruleSet['tests'] ?? null;
146+
}
114147
if (empty($requiredChecksNames)) {
115148
$this->staticCheckObjects = $this->allStaticCheckObjects;
116149
} else {
117-
for ($index = 0; $index < count($requiredChecksNames); $index++) {
118-
if (in_array($requiredChecksNames[$index], array_keys($this->allStaticCheckObjects))) {
119-
$this->staticCheckObjects[$requiredChecksNames[$index]] =
120-
$this->allStaticCheckObjects[$requiredChecksNames[$index]];
121-
} else {
122-
$invalidCheckNames[] = $requiredChecksNames[$index];
123-
}
150+
$this->validateTestNames($requiredChecksNames);
151+
}
152+
}
153+
154+
/**
155+
* Validates that all passed in static-check names match an existing static check
156+
* @param string[] $requiredChecksNames
157+
* @return void
158+
*/
159+
private function validateTestNames($requiredChecksNames)
160+
{
161+
$invalidCheckNames = [];
162+
for ($index = 0; $index < count($requiredChecksNames); $index++) {
163+
if (in_array($requiredChecksNames[$index], array_keys($this->allStaticCheckObjects))) {
164+
$this->staticCheckObjects[$requiredChecksNames[$index]] =
165+
$this->allStaticCheckObjects[$requiredChecksNames[$index]];
166+
} else {
167+
$invalidCheckNames[] = $requiredChecksNames[$index];
124168
}
125169
}
126170

127171
if (!empty($invalidCheckNames)) {
128172
throw new InvalidArgumentException(
129-
"Invalid static check script(s): " . implode(', ', $invalidCheckNames) . "."
173+
'Invalid static check script(s): ' . implode(', ', $invalidCheckNames) . '.'
130174
);
131175
}
132176
}
177+
178+
/**
179+
* Parses and sets local ruleSet. If not found, simply returns and lets script continue.
180+
* @return void;
181+
*/
182+
private function parseRulesetJson()
183+
{
184+
$pathAddition = "/dev/tests/acceptance/";
185+
// MFTF is both NOT attached and no MAGENTO_BP defined in .env
186+
if (MAGENTO_BP === FW_BP) {
187+
$pathAddition = "/dev/";
188+
}
189+
$pathToRuleset = MAGENTO_BP . $pathAddition . "staticRuleset.json";
190+
if (!file_exists($pathToRuleset)) {
191+
$this->ioStyle->text("No ruleset under $pathToRuleset" . PHP_EOL);
192+
return;
193+
}
194+
$this->ioStyle->text("Using ruleset under $pathToRuleset" . PHP_EOL);
195+
$this->ruleSet = json_decode(file_get_contents($pathToRuleset), true);
196+
}
133197
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\FunctionalTestingFramework\StaticCheck;
8+
9+
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Finder\Finder;
11+
use Exception;
12+
use Magento\FunctionalTestingFramework\Util\Script\ScriptUtil;
13+
use Symfony\Component\Finder\SplFileInfo;
14+
15+
/**
16+
* Class PrExcludeGroupStaticCheck
17+
* @package Magento\FunctionalTestingFramework\StaticCheck
18+
*/
19+
class PrExcludeGroupStaticCheck implements StaticCheckInterface
20+
{
21+
const GROUP_NAME = 'pr_exclude';
22+
const ERROR_LOG_FILENAME = 'mftf-pr-exclude-usage-checks';
23+
const ERROR_LOG_MESSAGE = 'MFTF pr_exclude Group Usage Check';
24+
25+
/**
26+
* Array containing all errors found after running the execute() function.
27+
* @var array
28+
*/
29+
private $errors = [];
30+
31+
/**
32+
* String representing the output summary found after running the execute() function.
33+
* @var string
34+
*/
35+
private $output;
36+
37+
/**
38+
* ScriptUtil instance
39+
*
40+
* @var ScriptUtil
41+
*/
42+
private $scriptUtil;
43+
44+
/**
45+
* Test xml files to scan
46+
*
47+
* @var Finder|array
48+
*/
49+
private $testXmlFiles = [];
50+
51+
/**
52+
* Action group xml files to scan
53+
*
54+
* @var Finder|array
55+
*/
56+
private $actionGroupXmlFiles = [];
57+
58+
/**
59+
* Suite xml files to scan
60+
*
61+
* @var Finder|array
62+
*/
63+
private $suiteXmlFiles = [];
64+
65+
/**
66+
* Root suite xml files to scan
67+
*
68+
* @var Finder|array
69+
*/
70+
private $rootSuiteXmlFiles = [];
71+
72+
/**
73+
* Checks usage of pause action in action groups, tests and suites and prints out error to file.
74+
*
75+
* @param InputInterface $input
76+
* @return void
77+
* @throws Exception
78+
*/
79+
public function execute(InputInterface $input)
80+
{
81+
$this->scriptUtil = new ScriptUtil();
82+
$modulePaths = [];
83+
$path = $input->getOption('path');
84+
if ($path) {
85+
if (!realpath($path)) {
86+
throw new \InvalidArgumentException('Invalid --path option: ' . $path);
87+
}
88+
$modulePaths[] = realpath($path);
89+
} else {
90+
$modulePaths = $this->scriptUtil->getAllModulePaths();
91+
}
92+
93+
$this->testXmlFiles = $this->scriptUtil->getModuleXmlFilesByScope($modulePaths, 'Test');
94+
$this->errors = [];
95+
$this->errors += $this->validatePrExcludeGroupUsageInTests($this->testXmlFiles);
96+
97+
$this->output = $this->scriptUtil->printErrorsToFile(
98+
$this->errors,
99+
StaticChecksList::getErrorFilesPath() . DIRECTORY_SEPARATOR . self::ERROR_LOG_FILENAME . '.txt',
100+
self::ERROR_LOG_MESSAGE
101+
);
102+
}
103+
104+
/**
105+
* Finds usages of pr_exclude group in test files
106+
* @param array $testXmlFiles
107+
* @return array
108+
*/
109+
private function validatePrExcludeGroupUsageInTests($testXmlFiles)
110+
{
111+
$testErrors = [];
112+
foreach ($testXmlFiles as $filePath) {
113+
$domDocument = new \DOMDocument();
114+
$domDocument->load($filePath);
115+
$test = $domDocument->getElementsByTagName('test')->item(0);
116+
if ($this->isViolatingPrExcludeTests($test)) {
117+
$testErrors = array_merge($testErrors, $this->setErrorOutput($filePath));
118+
}
119+
}
120+
return $testErrors;
121+
}
122+
123+
/**
124+
* Finds violating pr_exclude group
125+
* @param \DomNode $entity
126+
* @return bool
127+
*/
128+
private function isViolatingPrExcludeTests($entity)
129+
{
130+
$violation = false;
131+
$references = $entity->getElementsByTagName('group');
132+
133+
foreach ($references as $reference) {
134+
$groupValue = $reference->getAttribute('value');
135+
if ($groupValue === self::GROUP_NAME) {
136+
$violation = true;
137+
break;
138+
}
139+
}
140+
141+
return $violation;
142+
}
143+
144+
/**
145+
* Return array containing all errors found after running the execute() function.
146+
* @return array
147+
*/
148+
public function getErrors()
149+
{
150+
return $this->errors;
151+
}
152+
153+
/**
154+
* Return string of a short human readable result of the check. For example: "No errors found."
155+
* @return string
156+
*/
157+
public function getOutput()
158+
{
159+
return $this->output;
160+
}
161+
162+
/**
163+
* Build and return error output
164+
*
165+
* @param SplFileInfo $path
166+
* @return mixed
167+
*/
168+
private function setErrorOutput($path)
169+
{
170+
$testErrors = [];
171+
172+
$filePath = $this->getFilePath($path->getRealPath());
173+
174+
// Build error output
175+
$errorOutput = "\nFile \"{$filePath}\"";
176+
$errorOutput .= "\ncontains group 'pr_exclude' which is not allowed.\n";
177+
$testErrors[$filePath][] = $errorOutput;
178+
179+
return $testErrors;
180+
}
181+
182+
/**
183+
* Return relative path to files.
184+
* @param string $fileNames
185+
* @return string
186+
*/
187+
private function getFilePath($fileNames)
188+
{
189+
if (!empty($fileNames)) {
190+
$relativeFileNames = ltrim(
191+
str_replace(MAGENTO_BP, '', $fileNames)
192+
);
193+
if (!empty($relativeFileNames)) {
194+
return $relativeFileNames;
195+
}
196+
}
197+
return $fileNames;
198+
}
199+
}

0 commit comments

Comments
 (0)