From 0331f7eec902b9c04780129c905b30e0416abf2f Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Wed, 22 Jan 2020 09:01:30 -0600 Subject: [PATCH 01/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments --- .../StaticCheck/StaticChecksList.php | 1 + .../StaticCheck/UnusedArgumentsCheck.php | 212 ++++++++++++++++++ .../Handlers/ActionGroupObjectHandler.php | 5 +- 3 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php index f3cf20739..83f06225e 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php @@ -29,6 +29,7 @@ public function __construct(array $checks = []) { $this->checks = [ 'testDependencies' => new TestDependencyCheck(), + 'unusedArgumentsCheck' => new UnusedArgumentsCheck(), ] + $checks; } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php new file mode 100644 index 000000000..0dc74171c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php @@ -0,0 +1,212 @@ +initActionGroups(); + + $unusedArgumentList = $this->buildUnusedArgumentList($actionGroups); + + $this->errors += $this->setErrorOutput($unusedArgumentList); + + $this->output = $this->printErrorsToFile(); + + } + + /** + * Return array containing all errors found after running the execute() function. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + * Return string of a short human readable result of the check. For example: "No unused arguments found." + * @return string + */ + public function getOutput() + { + return $this->output; + } + + /** + * Builds array of action groups => unused arguments + * @param $actionGroups + * @return array + */ + private function buildUnusedArgumentList($actionGroups) { + + $actionGroupToArguments = []; + + foreach ($actionGroups as $actionGroup) { + $unusedArguments = $this->findUnusedArguments($actionGroup); + if(!empty($unusedArguments)) { + $actionGroupToArguments[$actionGroup->getFilename()][$actionGroup->getName()] = $unusedArguments; + } + } + return $actionGroupToArguments; + } + + /** + * Returns unused arguments in an action group. + * @param $actionGroup + * @return array + */ + private function findUnusedArguments($actionGroup) { + + $unusedArguments = []; + //extract all action attribute values + $actionAttributeValues = $this->getAllActionAttributeValues($actionGroup); + $argumentList = $actionGroup->getArguments(); + foreach ($argumentList as $argument) { + $argumentName = $argument->getName(); + //pattern to match all argument references + $pattern = '(.*\.*[\W]+(?getName(); + } + return $unusedArguments; + } + + /** + * Returns array of all action attribute values in an action group. + * @param $actionGroup + * @return array + */ + private function getAllActionAttributeValues($actionGroup) { + + $allAttributeValues = []; + $actions = $actionGroup->getActions(); + foreach ($actions as $action) { + $actionAttributeValues = $this->extractAttributeValues($action); + $allAttributeValues = array_merge($allAttributeValues, $actionAttributeValues); + } + return array_unique($allAttributeValues); + } + + + /** + * Builds and returns flattened attribute value list for an action. + * @param $action + * @return array + */ + private function extractAttributeValues($action) { + + $flattenedAttributeValues = []; + $actionAttributes = $action->getCustomActionAttributes(); + //check if action has nodes eg. expectedResult, actualResult and flatten array + foreach ($actionAttributes as $attributeName => $attributeValue) { + if (is_array($attributeValue)) { + $flattenedAttributeValues = array_merge($flattenedAttributeValues, array_values($attributeValue)); + } + else { + $flattenedAttributeValues[] = $attributeValue; + } + } + return $flattenedAttributeValues; + } + + /** + * Builds and returns error output for unused arguments + * + * @param array $unusedArgumentList + * @return mixed + */ + private function setErrorOutput($unusedArgumentList) + { + $testErrors = []; + + if (!empty($unusedArgumentList)) { + // Build error output + foreach ($unusedArgumentList as $path => $actionGroupToArguments) { + + $errorOutput = "\nFile \"{$path}\""; + $errorOutput .= "\ncontains action group(s) with unused arguments.\n\t\t"; + + foreach ($actionGroupToArguments as $actionGroup => $arguments) { + $errorOutput .= "\n\t {$actionGroup} has unused argument(s): " . implode(", ", $arguments); + } + $testErrors[$path][] = $errorOutput; + } + } + return $testErrors; + } + + /** + * Prints out given errors to file, and returns summary result string + * @return string + */ + private function printErrorsToFile() + { + $errors = $this->getErrors(); + + if (empty($errors)) { + return "No unused arguments found."; + } + + $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-arguments-checks.txt"; + $fileResource = fopen($outputPath, 'w'); + $header = "MFTF ActionGroup Arguments Check:\n"; + fwrite($fileResource, $header); + + foreach ($errors as $test => $error) { + fwrite($fileResource, $error[0] . PHP_EOL); + } + + fclose($fileResource); + $errorCount = count($errors); + $output = "Unused arguments found across {$errorCount} actionGroup(s). Error details output to {$outputPath}"; + + return $output; + } + +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php index 131ae0a26..91f1f2b0d 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php @@ -99,10 +99,10 @@ public function getAllObjects(): array /** * Method which populates field array with objects from parsed action_group.xml * - * @return void + * @return array * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ - private function initActionGroups() + public function initActionGroups() { $actionGroupParser = ObjectManagerFactory::getObjectManager()->create(ActionGroupDataParser::class); $parsedActionGroups = $actionGroupParser->readActionGroupData(); @@ -118,6 +118,7 @@ private function initActionGroups() $this->actionGroups[$actionGroupName] = $actionGroupObjectExtractor->extractActionGroup($actionGroupData); } + return $this->actionGroups; } /** From 4a4fc38df8dc59f10a3ee2228a04f8ebad238685 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Wed, 22 Jan 2020 10:59:29 -0600 Subject: [PATCH 02/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments modified regex, merged develop, fixed static checks --- .../StaticCheck/UnusedArgumentsCheck.php | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php index 0dc74171c..ffec45487 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php @@ -8,6 +8,8 @@ use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; +use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Symfony\Component\Console\Input\InputInterface; use Exception; @@ -35,7 +37,7 @@ class UnusedArgumentsCheck implements StaticCheckInterface * Checks unused arguments in action groups and prints out error to file. * * @param InputInterface $input - * @return string + * @return void * @throws Exception; */ public function execute(InputInterface $input) @@ -55,7 +57,6 @@ public function execute(InputInterface $input) $this->errors += $this->setErrorOutput($unusedArgumentList); $this->output = $this->printErrorsToFile(); - } /** @@ -78,16 +79,16 @@ public function getOutput() /** * Builds array of action groups => unused arguments - * @param $actionGroups - * @return array + * @param array $actionGroups + * @return array $actionGroupToArguments */ - private function buildUnusedArgumentList($actionGroups) { - + private function buildUnusedArgumentList($actionGroups) + { $actionGroupToArguments = []; foreach ($actionGroups as $actionGroup) { $unusedArguments = $this->findUnusedArguments($actionGroup); - if(!empty($unusedArguments)) { + if (!empty($unusedArguments)) { $actionGroupToArguments[$actionGroup->getFilename()][$actionGroup->getName()] = $unusedArguments; } } @@ -96,11 +97,11 @@ private function buildUnusedArgumentList($actionGroups) { /** * Returns unused arguments in an action group. - * @param $actionGroup - * @return array + * @param ActionGroupObject $actionGroup + * @return array $unusedArguments */ - private function findUnusedArguments($actionGroup) { - + private function findUnusedArguments($actionGroup) + { $unusedArguments = []; //extract all action attribute values $actionAttributeValues = $this->getAllActionAttributeValues($actionGroup); @@ -108,7 +109,7 @@ private function findUnusedArguments($actionGroup) { foreach ($argumentList as $argument) { $argumentName = $argument->getName(); //pattern to match all argument references - $pattern = '(.*\.*[\W]+(?getActions(); foreach ($actions as $action) { @@ -133,22 +134,20 @@ private function getAllActionAttributeValues($actionGroup) { return array_unique($allAttributeValues); } - /** * Builds and returns flattened attribute value list for an action. - * @param $action - * @return array + * @param ActionObject $action + * @return array $flattenedAttributeValues */ - private function extractAttributeValues($action) { - + private function extractAttributeValues($action) + { $flattenedAttributeValues = []; $actionAttributes = $action->getCustomActionAttributes(); //check if action has nodes eg. expectedResult, actualResult and flatten array foreach ($actionAttributes as $attributeName => $attributeValue) { if (is_array($attributeValue)) { $flattenedAttributeValues = array_merge($flattenedAttributeValues, array_values($attributeValue)); - } - else { + } else { $flattenedAttributeValues[] = $attributeValue; } } @@ -158,7 +157,7 @@ private function extractAttributeValues($action) { /** * Builds and returns error output for unused arguments * - * @param array $unusedArgumentList + * @param array $unusedArgumentList * @return mixed */ private function setErrorOutput($unusedArgumentList) @@ -168,7 +167,6 @@ private function setErrorOutput($unusedArgumentList) if (!empty($unusedArgumentList)) { // Build error output foreach ($unusedArgumentList as $path => $actionGroupToArguments) { - $errorOutput = "\nFile \"{$path}\""; $errorOutput .= "\ncontains action group(s) with unused arguments.\n\t\t"; @@ -208,5 +206,4 @@ private function printErrorsToFile() return $output; } - } From 58f0da61520522b01f3eadb2161fb4035bdc6435 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Wed, 22 Jan 2020 11:08:57 -0600 Subject: [PATCH 03/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments Documentation + fixed static checks --- docs/commands/mftf.md | 1 + .../StaticCheck/UnusedArgumentsCheck.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index 642bbb1bd..bb0cca3fb 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -468,6 +468,7 @@ Runs all MFTF static-checks on the test codebase that MFTF is currently attached #### Existing static checks * Test Dependency: Checks that test dependencies do not violate Magento module's composer dependencies. +* Unused Arguments: Checks that action groups do not have unused arguments. #### Usage diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php index ffec45487..b0a20959a 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php @@ -120,8 +120,8 @@ private function findUnusedArguments($actionGroup) /** * Returns array of all action attribute values in an action group. - * @param $actionGroup - * @return array + * @param ActionGroupObject $actionGroup + * @return array $allAttributeValues */ private function getAllActionAttributeValues($actionGroup) { From cc3601ae1f9262228a56415a09816fbb18fab024 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Fri, 24 Jan 2020 08:58:35 -0600 Subject: [PATCH 04/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments Refactoring + added logic to exclude arguments found in parent action group --- ...heck.php => ActionGroupArgumentsCheck.php} | 78 ++++++++++--------- .../StaticCheck/StaticCheckHelper.php | 36 +++++++++ .../StaticCheck/StaticChecksList.php | 2 +- .../StaticCheck/TestDependencyCheck.php | 35 ++------- 4 files changed, 84 insertions(+), 67 deletions(-) rename src/Magento/FunctionalTestingFramework/StaticCheck/{UnusedArgumentsCheck.php => ActionGroupArgumentsCheck.php} (75%) create mode 100644 src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php similarity index 75% rename from src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php rename to src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index b0a20959a..709d35341 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/UnusedArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -14,12 +14,13 @@ use Exception; /** - * Class UnusedArgumentsCheck + * Class ActionGroupArgumentsCheck * @package Magento\FunctionalTestingFramework\StaticCheck - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class UnusedArgumentsCheck implements StaticCheckInterface +class ActionGroupArgumentsCheck implements StaticCheckInterface { + const ERROR_LOG_FILENAME = 'mftf-arguments-checks'; + const ERROR_LOG_MESSAGE = 'MFTF Unused Arguments Check'; /** * Array containing all errors found after running the execute() function. @@ -38,10 +39,11 @@ class UnusedArgumentsCheck implements StaticCheckInterface * * @param InputInterface $input * @return void - * @throws Exception; + * @throws Exception */ public function execute(InputInterface $input) { + MftfApplicationConfig::create( true, MftfApplicationConfig::UNIT_TEST_PHASE, @@ -56,7 +58,8 @@ public function execute(InputInterface $input) $this->errors += $this->setErrorOutput($unusedArgumentList); - $this->output = $this->printErrorsToFile(); + $this->output = StaticCheckHelper::printErrorsToFile($this->errors, + self::ERROR_LOG_FILENAME, self::ERROR_LOG_MESSAGE); } /** @@ -109,15 +112,45 @@ private function findUnusedArguments($actionGroup) foreach ($argumentList as $argument) { $argumentName = $argument->getName(); //pattern to match all argument references - $pattern = '(.*[\W]+(?getName(); + //exclude arguments that are also defined in parent action group for extending action groups + if ($this->isParentActionGroupArgument($argument, $actionGroup)) { + continue; + } + $unusedArguments[] = $argumentName; } return $unusedArguments; } + /** + * Checks if the argument is also defined in the parent for extending action groups. + * @param string $argument + * @param ActionGroupObject $actionGroup + * @return bool + */ + private function isParentActionGroupArgument($argument, $actionGroup) { + + if ($actionGroup->getParentName() !== null) { + $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroup->getParentName()); + $parentArguments = $parentActionGroup->getArguments(); + if ($parentArguments !== null) { + return in_array($argument, $parentArguments); + } + return false; + } + } + /** * Returns array of all action attribute values in an action group. * @param ActionGroupObject $actionGroup @@ -163,7 +196,6 @@ private function extractAttributeValues($action) private function setErrorOutput($unusedArgumentList) { $testErrors = []; - if (!empty($unusedArgumentList)) { // Build error output foreach ($unusedArgumentList as $path => $actionGroupToArguments) { @@ -178,32 +210,4 @@ private function setErrorOutput($unusedArgumentList) } return $testErrors; } - - /** - * Prints out given errors to file, and returns summary result string - * @return string - */ - private function printErrorsToFile() - { - $errors = $this->getErrors(); - - if (empty($errors)) { - return "No unused arguments found."; - } - - $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-arguments-checks.txt"; - $fileResource = fopen($outputPath, 'w'); - $header = "MFTF ActionGroup Arguments Check:\n"; - fwrite($fileResource, $header); - - foreach ($errors as $test => $error) { - fwrite($fileResource, $error[0] . PHP_EOL); - } - - fclose($fileResource); - $errorCount = count($errors); - $output = "Unused arguments found across {$errorCount} actionGroup(s). Error details output to {$outputPath}"; - - return $output; - } } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php new file mode 100644 index 000000000..dc8803ceb --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php @@ -0,0 +1,36 @@ + $error) { + fwrite($fileResource, $error[0] . PHP_EOL); + } + + fclose($fileResource); + $errorCount = count($errors); + $output = $message . ": Errors found across {$errorCount} file(s). Error details output to {$outputPath}"; + + return $output; + } +} diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php index 83f06225e..ffa63389d 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php @@ -29,7 +29,7 @@ public function __construct(array $checks = []) { $this->checks = [ 'testDependencies' => new TestDependencyCheck(), - 'unusedArgumentsCheck' => new UnusedArgumentsCheck(), + 'actionGroupArguments' => new ActionGroupArgumentsCheck(), ] + $checks; } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 19725dc18..dff24b0ed 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\StaticCheck; +use Magento\FunctionalTestingFramework\StaticCheck\StaticCheckHelper; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; @@ -32,6 +33,9 @@ class TestDependencyCheck implements StaticCheckInterface const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/'; const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/'; + const ERROR_LOG_FILENAME = 'mftf-dependency-checks'; + const ERROR_LOG_MESSAGE = 'MFTF File Dependency Check'; + /** * Array of FullModuleName => [dependencies] * @var array @@ -123,7 +127,8 @@ public function execute(InputInterface $input) $this->errors += $this->findErrorsInFileSet($dataXmlFiles); // hold on to the output and print any errors to a file - $this->output = $this->printErrorsToFile(); + $this->output = StaticCheckHelper::printErrorsToFile($this->errors, + self::ERROR_LOG_FILENAME, self::ERROR_LOG_MESSAGE); } /** @@ -461,32 +466,4 @@ private function findEntity($name) } return null; } - - /** - * Prints out given errors to file, and returns summary result string - * @return string - */ - private function printErrorsToFile() - { - $errors = $this->getErrors(); - - if (empty($errors)) { - return "No Dependency errors found."; - } - - $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-dependency-checks.txt"; - $fileResource = fopen($outputPath, 'w'); - $header = "MFTF File Dependency Check:\n"; - fwrite($fileResource, $header); - - foreach ($errors as $test => $error) { - fwrite($fileResource, $error[0] . PHP_EOL); - } - - fclose($fileResource); - $errorCount = count($errors); - $output = "Dependency errors found across {$errorCount} file(s). Error details output to {$outputPath}"; - - return $output; - } } From 4a306c265d4b29e11c23414f1edb674a51e843a3 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Fri, 24 Jan 2020 09:43:04 -0600 Subject: [PATCH 05/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments fixing unit tests --- .../StaticCheck/ActionGroupArgumentsCheck.php | 15 +++++++++------ .../StaticCheck/TestDependencyCheck.php | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index 709d35341..705b2103d 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -58,8 +58,11 @@ public function execute(InputInterface $input) $this->errors += $this->setErrorOutput($unusedArgumentList); - $this->output = StaticCheckHelper::printErrorsToFile($this->errors, - self::ERROR_LOG_FILENAME, self::ERROR_LOG_MESSAGE); + $this->output = StaticCheckHelper::printErrorsToFile( + $this->errors, + self::ERROR_LOG_FILENAME, + self::ERROR_LOG_MESSAGE + ); } /** @@ -135,12 +138,12 @@ private function findUnusedArguments($actionGroup) /** * Checks if the argument is also defined in the parent for extending action groups. - * @param string $argument + * @param string $argument * @param ActionGroupObject $actionGroup - * @return bool + * @return boolean */ - private function isParentActionGroupArgument($argument, $actionGroup) { - + private function isParentActionGroupArgument($argument, $actionGroup) + { if ($actionGroup->getParentName() !== null) { $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroup->getParentName()); $parentArguments = $parentActionGroup->getArguments(); diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index dff24b0ed..2b309c015 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -127,8 +127,11 @@ public function execute(InputInterface $input) $this->errors += $this->findErrorsInFileSet($dataXmlFiles); // hold on to the output and print any errors to a file - $this->output = StaticCheckHelper::printErrorsToFile($this->errors, - self::ERROR_LOG_FILENAME, self::ERROR_LOG_MESSAGE); + $this->output = StaticCheckHelper::printErrorsToFile( + $this->errors, + self::ERROR_LOG_FILENAME, + self::ERROR_LOG_MESSAGE + ); } /** From f4b2d955f2ab8687b4d92721a5a6fc59196fbc7b Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Fri, 24 Jan 2020 10:04:55 -0600 Subject: [PATCH 06/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments fixing unit tests --- .../StaticCheck/StaticCheckHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php index dc8803ceb..5e3e8520d 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php @@ -9,7 +9,7 @@ class StaticCheckHelper { /** * Prints out given errors to file, and returns summary result string - * @param array $errors + * @param array $errors * @param string $filename * @param string $message * @return string From 731a7703e298d12cfd7208eaf5047bc830675a2b Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Sun, 26 Jan 2020 22:12:42 -0600 Subject: [PATCH 07/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments Refactored to read xml content from files directly to avoid flagging arguments due to step removals during merging/extensions. --- .../StaticCheck/ActionGroupArgumentsCheck.php | 145 ++++++++---------- .../StaticCheck/StaticCheckHelper.php | 21 +++ .../StaticCheck/TestDependencyCheck.php | 25 +-- .../Handlers/ActionGroupObjectHandler.php | 5 +- 4 files changed, 94 insertions(+), 102 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index 705b2103d..8e630bd63 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -9,8 +9,9 @@ use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; -use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Symfony\Component\Console\Input\InputInterface; +use Magento\FunctionalTestingFramework\Util\ModuleResolver; +use Symfony\Component\Finder\Finder; use Exception; /** @@ -19,6 +20,11 @@ */ class ActionGroupArgumentsCheck implements StaticCheckInterface { + + const ACTIONGROUP_XML_REGEX_PATTERN = '/).)*/mxs'; + const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/mxs'; + const ACTIONGROUP_NAME_REGEX_PATTERN = '/initActionGroups(); + $allModules = ModuleResolver::getInstance()->getModulesPath(); - $unusedArgumentList = $this->buildUnusedArgumentList($actionGroups); + $actionGroupXmlFiles = StaticCheckHelper::buildFileList( + $allModules, + DIRECTORY_SEPARATOR . 'ActionGroup' . DIRECTORY_SEPARATOR + ); - $this->errors += $this->setErrorOutput($unusedArgumentList); + $this->errors = $this->findErrorsInFileSet($actionGroupXmlFiles); $this->output = StaticCheckHelper::printErrorsToFile( $this->errors, @@ -84,7 +92,24 @@ public function getOutput() } /** - * Builds array of action groups => unused arguments + * Finds all unused arguments in given set of actionGroup files + * @param Finder $files + * @return array $testErrors + */ + private function findErrorsInFileSet($files) + { + $actionGroupErrors = []; + foreach ($files as $filePath) { + $contents = file_get_contents($filePath); + preg_match_all(self::ACTIONGROUP_XML_REGEX_PATTERN, $contents, $actionGroups); + $actionGroupToArguments = $this->buildUnusedArgumentList($actionGroups[0]); + $actionGroupErrors += $this->setErrorOutput($actionGroupToArguments, $filePath); + } + return $actionGroupErrors; + } + + /** + * Builds array of action group => unused arguments * @param array $actionGroups * @return array $actionGroupToArguments */ @@ -92,46 +117,48 @@ private function buildUnusedArgumentList($actionGroups) { $actionGroupToArguments = []; - foreach ($actionGroups as $actionGroup) { - $unusedArguments = $this->findUnusedArguments($actionGroup); + foreach ($actionGroups as $actionGroupXml) { + preg_match(self::ACTIONGROUP_NAME_REGEX_PATTERN, $actionGroupXml, $actionGroupName); + $unusedArguments = $this->findUnusedArguments($actionGroupXml); if (!empty($unusedArguments)) { - $actionGroupToArguments[$actionGroup->getFilename()][$actionGroup->getName()] = $unusedArguments; + $actionGroupToArguments[$actionGroupName[1]] = $unusedArguments; } } return $actionGroupToArguments; } /** - * Returns unused arguments in an action group. - * @param ActionGroupObject $actionGroup - * @return array $unusedArguments + * @param $actionGroupXml + * @return array */ - private function findUnusedArguments($actionGroup) + private function findUnusedArguments($actionGroupXml) { $unusedArguments = []; - //extract all action attribute values - $actionAttributeValues = $this->getAllActionAttributeValues($actionGroup); - $argumentList = $actionGroup->getArguments(); - foreach ($argumentList as $argument) { - $argumentName = $argument->getName(); + + preg_match_all(self::ACTIONGROUP_ARGUMENT_REGEX_PATTERN, $actionGroupXml, $arguments); + preg_match(self::ACTIONGROUP_NAME_REGEX_PATTERN, $actionGroupXml, $actionGroupName); + + $actionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroupName[1]); + + foreach ($arguments[1] as $argument) { //pattern to match all argument references $patterns = [ - '(\{{2}' . $argumentName . '(\.[a-zA-Z0-9_\[\]\(\).,\'\/ ]+)?}{2})', - '([(,\s\']' . $argumentName . '(\.[a-zA-Z0-9_\[\]]+)?[),\s\'])' + '(\{{2}' . $argument . '(\.[a-zA-Z0-9_\[\]\(\).,\'\/ ]+)?}{2})', + '([(,\s\']' . $argument . '(\.[a-zA-Z0-9_\[\]]+)?[),\s\'])' ]; // matches entity references - if (preg_grep($patterns[0], $actionAttributeValues)) { + if (preg_match($patterns[0], $actionGroupXml)) { continue; } //matches parametrized references - if (preg_grep($patterns[1], $actionAttributeValues)) { + if (preg_match($patterns[1], $actionGroupXml)) { continue; } - //exclude arguments that are also defined in parent action group for extending action groups + //for extending action groups, exclude arguments that are also defined in parent action group if ($this->isParentActionGroupArgument($argument, $actionGroup)) { continue; } - $unusedArguments[] = $argumentName; + $unusedArguments[] = $argument; } return $unusedArguments; } @@ -147,70 +174,34 @@ private function isParentActionGroupArgument($argument, $actionGroup) if ($actionGroup->getParentName() !== null) { $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroup->getParentName()); $parentArguments = $parentActionGroup->getArguments(); - if ($parentArguments !== null) { - return in_array($argument, $parentArguments); - } - return false; - } - } - - /** - * Returns array of all action attribute values in an action group. - * @param ActionGroupObject $actionGroup - * @return array $allAttributeValues - */ - private function getAllActionAttributeValues($actionGroup) - { - $allAttributeValues = []; - $actions = $actionGroup->getActions(); - foreach ($actions as $action) { - $actionAttributeValues = $this->extractAttributeValues($action); - $allAttributeValues = array_merge($allAttributeValues, $actionAttributeValues); - } - return array_unique($allAttributeValues); - } - - /** - * Builds and returns flattened attribute value list for an action. - * @param ActionObject $action - * @return array $flattenedAttributeValues - */ - private function extractAttributeValues($action) - { - $flattenedAttributeValues = []; - $actionAttributes = $action->getCustomActionAttributes(); - //check if action has nodes eg. expectedResult, actualResult and flatten array - foreach ($actionAttributes as $attributeName => $attributeValue) { - if (is_array($attributeValue)) { - $flattenedAttributeValues = array_merge($flattenedAttributeValues, array_values($attributeValue)); - } else { - $flattenedAttributeValues[] = $attributeValue; + foreach ($parentArguments as $parentArgument) { + if ($argument === $parentArgument->getName()) { + return true; + } } } - return $flattenedAttributeValues; + return false; } /** - * Builds and returns error output for unused arguments + * Builds and returns error output for violating references * - * @param array $unusedArgumentList + * @param array $actionGroupToArguments + * @param string $path * @return mixed */ - private function setErrorOutput($unusedArgumentList) + private function setErrorOutput($actionGroupToArguments, $path) { - $testErrors = []; - if (!empty($unusedArgumentList)) { + $actionGroupErrors = []; + if (!empty($actionGroupToArguments)) { // Build error output - foreach ($unusedArgumentList as $path => $actionGroupToArguments) { - $errorOutput = "\nFile \"{$path}\""; - $errorOutput .= "\ncontains action group(s) with unused arguments.\n\t\t"; - - foreach ($actionGroupToArguments as $actionGroup => $arguments) { - $errorOutput .= "\n\t {$actionGroup} has unused argument(s): " . implode(", ", $arguments); - } - $testErrors[$path][] = $errorOutput; + $errorOutput = "\nFile \"{$path->getRealPath()}\""; + $errorOutput .= "\ncontains action group(s) with unused arguments.\n\t\t"; + foreach ($actionGroupToArguments as $actionGroup => $arguments) { + $errorOutput .= "\n\t {$actionGroup} has unused argument(s): " . implode(", ", $arguments); } + $actionGroupErrors[$path->getRealPath()][] = $errorOutput; } - return $testErrors; + return $actionGroupErrors; } } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php index 5e3e8520d..6430774d4 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php @@ -5,6 +5,8 @@ */ namespace Magento\FunctionalTestingFramework\StaticCheck; +use Symfony\Component\Finder\Finder; + class StaticCheckHelper { /** @@ -33,4 +35,23 @@ public static function printErrorsToFile($errors, $filename, $message) return $output; } + + /** + * Builds list of all XML files in given modulePaths + path given + * @param array $modulePaths + * @param string $path + * @return Finder + */ + public static function buildFileList($modulePaths, $path) + { + $finder = new Finder(); + foreach ($modulePaths as $modulePath) { + if (!realpath($modulePath . $path)) { + continue; + } + $finder->files()->in($modulePath . $path)->name("*.xml"); + } + return $finder->files(); + } + } diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 2b309c015..3c3ed6a37 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -6,7 +6,6 @@ namespace Magento\FunctionalTestingFramework\StaticCheck; -use Magento\FunctionalTestingFramework\StaticCheck\StaticCheckHelper; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; @@ -117,9 +116,9 @@ public function execute(InputInterface $input) DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR, ]; // These files can contain references to other modules. - $testXmlFiles = $this->buildFileList($allModules, $filePaths[0]); - $actionGroupXmlFiles = $this->buildFileList($allModules, $filePaths[1]); - $dataXmlFiles= $this->buildFileList($allModules, $filePaths[2]); + $testXmlFiles = StaticCheckHelper::buildFileList($allModules, $filePaths[0]); + $actionGroupXmlFiles = StaticCheckHelper::buildFileList($allModules, $filePaths[1]); + $dataXmlFiles= StaticCheckHelper::buildFileList($allModules, $filePaths[2]); $this->errors = []; $this->errors += $this->findErrorsInFileSet($testXmlFiles); @@ -421,24 +420,6 @@ private function getModuleDependenciesFromReferences($array) return $filenames; } - /** - * Builds list of all XML files in given modulePaths + path given - * @param string $modulePaths - * @param string $path - * @return Finder - */ - private function buildFileList($modulePaths, $path) - { - $finder = new Finder(); - foreach ($modulePaths as $modulePath) { - if (!realpath($modulePath . $path)) { - continue; - } - $finder->files()->in($modulePath . $path)->name("*.xml"); - } - return $finder->files(); - } - /** * Attempts to find any MFTF entity by its name. Returns null if none are found. * @param string $name diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php index 91f1f2b0d..131ae0a26 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/ActionGroupObjectHandler.php @@ -99,10 +99,10 @@ public function getAllObjects(): array /** * Method which populates field array with objects from parsed action_group.xml * - * @return array + * @return void * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ - public function initActionGroups() + private function initActionGroups() { $actionGroupParser = ObjectManagerFactory::getObjectManager()->create(ActionGroupDataParser::class); $parsedActionGroups = $actionGroupParser->readActionGroupData(); @@ -118,7 +118,6 @@ public function initActionGroups() $this->actionGroups[$actionGroupName] = $actionGroupObjectExtractor->extractActionGroup($actionGroupData); } - return $this->actionGroups; } /** From 089b88d9d777318b0748b1536345b34e63057494 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Sun, 26 Jan 2020 22:31:29 -0600 Subject: [PATCH 08/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments fixed unit tests --- .../StaticCheck/ActionGroupArgumentsCheck.php | 8 +++++--- .../StaticCheck/StaticCheckHelper.php | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index 8e630bd63..6c5c34203 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -128,7 +128,8 @@ private function buildUnusedArgumentList($actionGroups) } /** - * @param $actionGroupXml + * Returns unused arguments in an action group + * @param string $actionGroupXml * @return array */ private function findUnusedArguments($actionGroupXml) @@ -171,8 +172,9 @@ private function findUnusedArguments($actionGroupXml) */ private function isParentActionGroupArgument($argument, $actionGroup) { - if ($actionGroup->getParentName() !== null) { - $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroup->getParentName()); + $parentActionGroupName = $actionGroup->getParentName(); + if ($parentActionGroupName !== null) { + $parentActionGroup = ActionGroupObjectHandler::getInstance()->getObject($parentActionGroupName); $parentArguments = $parentActionGroup->getArguments(); foreach ($parentArguments as $parentArgument) { if ($argument === $parentArgument->getName()) { diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php index 6430774d4..f534544fc 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckHelper.php @@ -38,7 +38,7 @@ public static function printErrorsToFile($errors, $filename, $message) /** * Builds list of all XML files in given modulePaths + path given - * @param array $modulePaths + * @param array $modulePaths * @param string $path * @return Finder */ @@ -53,5 +53,4 @@ public static function buildFileList($modulePaths, $path) } return $finder->files(); } - } From 28fe7572e30fe0f48a08587f2325d85edc4fa835 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Mon, 27 Jan 2020 13:47:45 -0600 Subject: [PATCH 09/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments added exception catch for incorrect action group definition --- docs/commands/mftf.md | 7 +++++-- .../StaticCheck/ActionGroupArgumentsCheck.php | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/commands/mftf.md b/docs/commands/mftf.md index 83c48507e..b2c36dae4 100644 --- a/docs/commands/mftf.md +++ b/docs/commands/mftf.md @@ -494,12 +494,15 @@ vendor/bin/mftf static-checks testDependencies ```bash vendor/bin/mftf static-checks actionGroupArguments ``` +```bash +vendor/bin/mftf static-checks testDependencies actionGroupArguments +``` #### Existing static checks * Test Dependency: Checks that test dependencies do not violate Magento module's composer dependencies. -* Unused Arguments: Checks that action groups do not have unused arguments. - +* Action Group Unused Arguments: Checks that action groups do not have unused arguments. + ### `upgrade:tests` Applies all the MFTF major version upgrade scripts to test components in the given path (`test.xml`, `data.xml`, etc). diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index 6c5c34203..2b25fcf65 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\StaticCheck; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject; use Symfony\Component\Console\Input\InputInterface; @@ -138,9 +139,10 @@ private function findUnusedArguments($actionGroupXml) preg_match_all(self::ACTIONGROUP_ARGUMENT_REGEX_PATTERN, $actionGroupXml, $arguments); preg_match(self::ACTIONGROUP_NAME_REGEX_PATTERN, $actionGroupXml, $actionGroupName); - - $actionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroupName[1]); - + try { + $actionGroup = ActionGroupObjectHandler::getInstance()->getObject($actionGroupName[1]); + } catch (XmlException $e){ + } foreach ($arguments[1] as $argument) { //pattern to match all argument references $patterns = [ From 1c7c6ced5563b36416e028384bdef909d497a887 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan Date: Mon, 27 Jan 2020 14:30:31 -0600 Subject: [PATCH 10/10] MQE-1676: Add a static-check that ensures action groups do not have unused arguments included $$argument.name$$ pattern + fixed unit tests --- .../StaticCheck/ActionGroupArgumentsCheck.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php index 2b25fcf65..32c7661fe 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/ActionGroupArgumentsCheck.php @@ -27,7 +27,7 @@ class ActionGroupArgumentsCheck implements StaticCheckInterface const ACTIONGROUP_NAME_REGEX_PATTERN = '/getObject($actionGroupName[1]); - } catch (XmlException $e){ + } catch (XmlException $e) { } foreach ($arguments[1] as $argument) { //pattern to match all argument references $patterns = [ '(\{{2}' . $argument . '(\.[a-zA-Z0-9_\[\]\(\).,\'\/ ]+)?}{2})', - '([(,\s\']' . $argument . '(\.[a-zA-Z0-9_\[\]]+)?[),\s\'])' + '([(,\s\'$$]' . $argument . '(\.[a-zA-Z0-9_$\[\]]+)?[),\s\'])' ]; // matches entity references if (preg_match($patterns[0], $actionGroupXml)) {