From 9c3465d4657abf29cd850708d741733f27bc216a Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Mon, 7 Jan 2019 08:45:49 -0600 Subject: [PATCH 01/10] MQE-810: Create a static test to validate references between modules - Set up ground work for test command --- .../Console/StaticChecksCommand.php | 56 +++++++++++++++++++ .../StaticChecks/StaticCheckInterface.php | 22 ++++++++ .../StaticChecks/StaticCheckListInterface.php | 20 +++++++ .../StaticChecks/StaticChecksList.php | 42 ++++++++++++++ .../StaticChecks/TestDependencyCheck.php | 33 +++++++++++ 5 files changed, 173 insertions(+) create mode 100644 src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php create mode 100644 src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckListInterface.php create mode 100644 src/Magento/FunctionalTestingFramework/StaticChecks/StaticChecksList.php create mode 100644 src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php diff --git a/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php new file mode 100644 index 000000000..fde306791 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/StaticChecksCommand.php @@ -0,0 +1,56 @@ +setName('static-checks') + ->setDescription('This command will run all static checks on xml test materials.'); + $this->staticChecksList = new StaticChecksList(); + } + + /** + * + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $staticCheckObjects = $this->staticChecksList->getStaticChecks(); + foreach ($staticCheckObjects as $staticCheck) { + $staticOutput = $staticCheck->execute($input); + LoggingUtil::getInstance()->getLogger(get_class($staticCheck))->info($staticOutput); + $output->writeln($staticOutput); + } + } +} diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php b/src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php new file mode 100644 index 000000000..45ebdea8d --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php @@ -0,0 +1,22 @@ +checks = [ + 'testDependencies' => new TestDependencyCheck(), + ] + $checks; + } + + /** + * {@inheritdoc} + */ + public function getStaticChecks() + { + return $this->checks; + } +} diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php new file mode 100644 index 000000000..af5957cf3 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php @@ -0,0 +1,33 @@ + Date: Tue, 26 Feb 2019 09:00:03 -0600 Subject: [PATCH 02/10] MQE-810: Create a static test to validate references between modules - Set up More ground work for test command --- src/Magento/FunctionalTestingFramework/Console/CommandList.php | 1 + .../{StaticChecks => StaticCheck}/StaticCheckInterface.php | 0 .../{StaticChecks => StaticCheck}/StaticCheckListInterface.php | 0 .../{StaticChecks => StaticCheck}/StaticChecksList.php | 0 .../{StaticChecks => StaticCheck}/TestDependencyCheck.php | 1 - 5 files changed, 1 insertion(+), 1 deletion(-) rename src/Magento/FunctionalTestingFramework/{StaticChecks => StaticCheck}/StaticCheckInterface.php (100%) rename src/Magento/FunctionalTestingFramework/{StaticChecks => StaticCheck}/StaticCheckListInterface.php (100%) rename src/Magento/FunctionalTestingFramework/{StaticChecks => StaticCheck}/StaticChecksList.php (100%) rename src/Magento/FunctionalTestingFramework/{StaticChecks => StaticCheck}/TestDependencyCheck.php (99%) diff --git a/src/Magento/FunctionalTestingFramework/Console/CommandList.php b/src/Magento/FunctionalTestingFramework/Console/CommandList.php index ce971146e..7623bea66 100644 --- a/src/Magento/FunctionalTestingFramework/Console/CommandList.php +++ b/src/Magento/FunctionalTestingFramework/Console/CommandList.php @@ -38,6 +38,7 @@ public function __construct(array $commands = []) 'run:failed' => new RunTestFailedCommand(), 'setup:env' => new SetupEnvCommand(), 'upgrade:tests' => new UpgradeTestsCommand(), + 'static-checks' => new StaticChecksCommand(), ] + $commands; } diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckInterface.php similarity index 100% rename from src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckInterface.php rename to src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckInterface.php diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckListInterface.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckListInterface.php similarity index 100% rename from src/Magento/FunctionalTestingFramework/StaticChecks/StaticCheckListInterface.php rename to src/Magento/FunctionalTestingFramework/StaticCheck/StaticCheckListInterface.php diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/StaticChecksList.php b/src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php similarity index 100% rename from src/Magento/FunctionalTestingFramework/StaticChecks/StaticChecksList.php rename to src/Magento/FunctionalTestingFramework/StaticCheck/StaticChecksList.php diff --git a/src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php similarity index 99% rename from src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php rename to src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index af5957cf3..603d52b00 100644 --- a/src/Magento/FunctionalTestingFramework/StaticChecks/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -26,7 +26,6 @@ public function execute(InputInterface $input) { // Check {{data.xml}}, {{section.element}}, {{page.url}} references - // Check actionGroup references } From a84ed84fae8d0cec45722608e635471aba316605 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Mon, 7 Jan 2019 10:08:34 -0600 Subject: [PATCH 03/10] MQE-810: Create a static test to validate references between modules - Added filename to all test objects --- .../Handlers/DataObjectHandler.php | 5 ++++- .../Objects/EntityDataObject.php | 20 +++++++++++++++++- .../DataGenerator/Util/DataExtensionUtil.php | 3 ++- .../Page/Handlers/SectionObjectHandler.php | 4 +++- .../Page/Objects/SectionObject.php | 10 ++++++++- .../Test/Objects/ActionGroupObject.php | 21 ++++++++++++++++++- .../Test/Util/ActionGroupObjectExtractor.php | 3 ++- .../Test/Util/ObjectExtensionUtil.php | 3 ++- 8 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index 7305fdc37..7cc9e8a0e 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -32,6 +32,7 @@ class DataObjectHandler implements ObjectHandlerInterface const _ENTITY_KEY = 'entityKey'; const _SEPARATOR = '->'; const _REQUIRED_ENTITY = 'requiredEntity'; + const _FILENAME = 'filename'; const DATA_NAME_ERROR_MSG = "Entity names cannot contain non alphanumeric characters.\tData='%s'"; /** @@ -134,6 +135,7 @@ private function processParserOutput($parserOutput) $uniquenessData = []; $vars = []; $parentEntity = null; + $filename = $rawEntity[self::_FILENAME] ?? null; if (array_key_exists(self::_DATA, $rawEntity)) { $data = $this->processDataElements($rawEntity); @@ -167,7 +169,8 @@ private function processParserOutput($parserOutput) $linkedEntities, $uniquenessData, $vars, - $parentEntity + $parentEntity, + $filename ); $entityDataObjects[$entityDataObject->getName()] = $entityDataObject; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index 1ba126e5f..7123b26d4 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -72,6 +72,12 @@ class EntityDataObject */ private $parentEntity; + /** + * String of filename + * @var string + */ + private $filename; + /** * Constructor * @@ -82,8 +88,9 @@ class EntityDataObject * @param string[] $uniquenessData * @param string[] $vars * @param string $parentEntity + * @param string $filename */ - public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = [], $parentEntity = null) + public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = [], $parentEntity = null, $filename) { $this->name = $name; $this->type = $type; @@ -95,6 +102,7 @@ public function __construct($name, $type, $data, $linkedEntities, $uniquenessDat $this->vars = $vars; $this->parentEntity = $parentEntity; + $this->filename = $filename; } /** @@ -107,6 +115,16 @@ public function getName() return $this->name; } + /** + * Getter for the Entity Filename + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } + /** * Get the type of this entity data object * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php index 1123531ed..2766b39a0 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Util/DataExtensionUtil.php @@ -92,7 +92,8 @@ public function extendEntity($entityObject) $newLinkedReferences, $newUniqueReferences, $newVarReferences, - $entityObject->getParentName() + $entityObject->getParentName(), + $entityObject->getFilename() ); return $extendedEntity; } diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php index b63d5b49b..61aaf3f08 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php @@ -22,6 +22,7 @@ class SectionObjectHandler implements ObjectHandlerInterface const LOCATOR_FUNCTION = 'locatorFunction'; const TIMEOUT = 'timeout'; const PARAMETERIZED = 'parameterized'; + const FILENAME = 'filename'; const SECTION_NAME_ERROR_MSG = "Section names cannot contain non alphanumeric characters.\tSection='%s'"; const ELEMENT_NAME_ERROR_MSG = "Element names cannot contain non alphanumeric characters.\tElement='%s'"; @@ -86,7 +87,8 @@ private function __construct() throw new XmlException($exception->getMessage() . " in Section '{$sectionName}'"); } - $this->sectionObjects[$sectionName] = new SectionObject($sectionName, $elements); + $filename = $sectionData[self::FILENAME]; + $this->sectionObjects[$sectionName] = new SectionObject($sectionName, $elements, $filename); } } diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php index 9ac641bca..d16f55dae 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php @@ -25,15 +25,23 @@ class SectionObject */ private $elements = []; + /** + * Filename of where the section came from + * + * @var string + */ + private $filename; + /** * SectionObject constructor. * @param string $name * @param array $elements */ - public function __construct($name, $elements) + public function __construct($name, $elements, $filename) { $this->name = $name; $this->elements = $elements; + $this->filename = $filename; } /** diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index 67c1ba25c..485b8cb9c 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -71,6 +71,13 @@ class ActionGroupObject */ private $parentActionGroup; + /** + * Filename where actionGroup came from + * + * @var string + */ + private $filename; + /** * ActionGroupObject constructor. * @@ -78,8 +85,9 @@ class ActionGroupObject * @param ArgumentObject[] $arguments * @param array $actions * @param string $parentActionGroup + * @param string $filename */ - public function __construct($name, $arguments, $actions, $parentActionGroup) + public function __construct($name, $arguments, $actions, $parentActionGroup, $filename) { $this->varAttributes = array_merge( ActionObject::SELECTOR_ENABLED_ATTRIBUTES, @@ -90,6 +98,7 @@ public function __construct($name, $arguments, $actions, $parentActionGroup) $this->arguments = $arguments; $this->parsedActions = $actions; $this->parentActionGroup = $parentActionGroup; + $this->filename = $filename; } /** @@ -409,6 +418,16 @@ public function getName() return $this->name; } + /** + * Getter for the Action Group Filename + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } + /** * Getter for the Parent Action Group Name * diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php index 04f739af6..6d7e67b38 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionGroupObjectExtractor.php @@ -76,7 +76,8 @@ public function extractActionGroup($actionGroupData) $actionGroupData[self::NAME], $arguments, $actions, - $actionGroupReference + $actionGroupReference, + $actionGroupData[self::FILENAME] ); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php index e67037940..4c3949ef3 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php @@ -131,7 +131,8 @@ public function extendActionGroup($actionGroupObject) $actionGroupObject->getName(), $extendedArguments, $newActions, - $actionGroupObject->getParentName() + $actionGroupObject->getParentName(), + $actionGroupObject->getFilename() ); return $extendedActionGroup; } From 61c76000e90df0b019a37cec17acc3d947671bad Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Tue, 26 Feb 2019 11:21:44 -0600 Subject: [PATCH 04/10] MQE-810: Create a static test to validate references between modules - Added filename to page/section --- .../Page/Handlers/PageObjectHandler.php | 4 +++- .../Page/Objects/PageObject.php | 21 ++++++++++++++++++- .../Page/Objects/SectionObject.php | 11 ++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php index caf2262fa..02c7bac66 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php @@ -20,6 +20,7 @@ class PageObjectHandler implements ObjectHandlerInterface const MODULE = 'module'; const PARAMETERIZED = 'parameterized'; const AREA = 'area'; + const FILENAME = 'filename'; const NAME_BLACKLIST_ERROR_MSG = "Page names cannot contain non alphanumeric characters.\tPage='%s'"; /** @@ -66,9 +67,10 @@ private function __construct() $module = $pageData[self::MODULE]; $sectionNames = array_keys($pageData[self::SECTION] ?? []); $parameterized = $pageData[self::PARAMETERIZED] ?? false; + $filename = $pageData[self::FILENAME] ?? null; $this->pageObjects[$pageName] = - new PageObject($pageName, $url, $module, $sectionNames, $parameterized, $area); + new PageObject($pageName, $url, $module, $sectionNames, $parameterized, $area, $filename); } } diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php index 3533a13f3..91bc241cb 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php @@ -58,6 +58,13 @@ class PageObject */ private $area; + /** + * Filename of where the page came from + * + * @var string + */ + private $filename; + /** * PageObject constructor. * @param string $name @@ -66,8 +73,9 @@ class PageObject * @param array $sections * @param boolean $parameterized * @param string $area + * @param string $filename */ - public function __construct($name, $url, $module, $sections, $parameterized, $area) + public function __construct($name, $url, $module, $sections, $parameterized, $area, $filename) { $this->name = $name; $this->url = $url; @@ -75,6 +83,7 @@ public function __construct($name, $url, $module, $sections, $parameterized, $ar $this->sectionNames = $sections; $this->parameterized = $parameterized; $this->area = $area; + $this->filename = $filename; } /** @@ -87,6 +96,16 @@ public function getName() return $this->name; } + /** + * Getter for the Page Filename + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } + /** * Getter for Page URL * diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php index d16f55dae..aae339417 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php @@ -36,6 +36,7 @@ class SectionObject * SectionObject constructor. * @param string $name * @param array $elements + * @param string $filename */ public function __construct($name, $elements, $filename) { @@ -54,6 +55,16 @@ public function getName() return $this->name; } + /** + * Getter for the Section Filename + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } + /** * Getter for an array containing all of a section's elements. * From efaaf8d135d2f79eaa4925d067db3d7d8e1769be Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 27 Feb 2019 09:13:24 -0600 Subject: [PATCH 05/10] MQE-810: Create a static test to validate references between modules - Implemented Check, only checks first filename found by MFTF. --- .../StaticCheck/TestDependencyCheck.php | 336 +++++++++++++++++- 1 file changed, 334 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 603d52b00..4f09cbb97 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -6,6 +6,16 @@ namespace Magento\FunctionalTestingFramework\StaticCheck; +use Magento\FunctionalTestingFramework\Config\Data; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler; +use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; +use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; +use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; +use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Util\ModuleResolver; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; @@ -16,17 +26,339 @@ */ class TestDependencyCheck implements StaticCheckInterface { + const EXTENDS_REGEX_PATTERN = '/extends=["\']([^\'"]*)/'; + const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/'; + + + /** + * Array of FullModuleName => [dependencies] + * @var array + */ + private $allDependencies; + + /** + * Array of FullModuleName => [dependencies], including flattened dependency tree + * @var array + */ + private $flattenedDependencies; + + /** + * Array of FullModuleName => PathToModule + * @var array + */ + private $moduleNameToPath; + + /** + * Array of FullModuleName => ComposerModuleName + * @var array + */ + private $moduleNameToComposerName; + + /** + * Transactional Array to keep track of what dependencies have already been extracted. + * @var array + */ + private $alreadyExtractedDependencies; + /** * Checks test dependencies, determined by references in tests versus the dependencies listed in the Magento module * * @param InputInterface $input * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(InputInterface $input) { - // Check {{data.xml}}, {{section.element}}, {{page.url}} references + MftfApplicationConfig::create( + true, + MftfApplicationConfig::UNIT_TEST_PHASE, + false, + false + ); + + ModuleResolver::getInstance()->getModulesPath(); + if (!class_exists('\Magento\Framework\Component\ComponentRegistrar')) { + return "TEST DEPENDENCY CHECK ABORTED: MFTF must be attached or pointing to Magento codebase."; + } + $registrar = new \Magento\Framework\Component\ComponentRegistrar(); + $this->moduleNameToPath = $registrar->getPaths(\Magento\Framework\Component\ComponentRegistrar::MODULE); + $this->moduleNameToComposerName = $this->buildModuleNameToComposerName($this->moduleNameToPath); + $this->flattenedDependencies = $this->buildComposerDependencyList(); + + $allModules = []; + + // Trim non-magento modules from search pool. + foreach (ModuleResolver::getInstance()->getModulesPath() as $module){ + $tempModule = rtrim($module, '/Test/Mftf'); + if (array_search($tempModule, $this->moduleNameToPath)) { + $allModules[] = $module; + } + } + + $testErrors = []; + + $filePaths = [ + DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR . 'ActionGroup' . DIRECTORY_SEPARATOR, + 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]); + + $testErrors += $this->findErrorsInFileSet($testXmlFiles); + $testErrors += $this->findErrorsInFileSet($actionGroupXmlFiles); + $testErrors += $this->findErrorsInFileSet($dataXmlFiles); + + //print all errors to file + return $this->printErrorsToFile($testErrors); + } + + private function findErrorsInFileSet($files) + { + $testErrors = []; + foreach ($files as $filePath) { + $modulePath = dirname(dirname(dirname(dirname($filePath)))); + $moduleFullName = array_search($modulePath, $this->moduleNameToPath) ?? null; + // Not a module, is either dev/tests/acceptance or loose folder with test materials + if ($moduleFullName == null) { + continue; + } + + $contents = file_get_contents($filePath); + $allEntities = []; + preg_match_all(ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN, $contents, $braceReferences); + preg_match_all(self::ACTIONGROUP_REGEX_PATTERN, $contents, $actionGroupReferences); + preg_match_all(self::EXTENDS_REGEX_PATTERN, $contents, $extendReferences); + + // Remove Duplicates + $braceReferences[0] = array_unique($braceReferences[0]); + $actionGroupReferences[1] = array_unique($actionGroupReferences[1]); + $braceReferences[1] = array_unique($braceReferences[1]); + + foreach ($braceReferences[0] as $reference) { + // trim `{{data.field}}` to `data` + preg_match('/{{([^.]+)/', $reference, $entityName); + $entity = $this->findEntity($entityName[1]); + if ($entity !== null) { + $allEntities[$entity->getName()] = $entity; + } + } + foreach ($actionGroupReferences[1] as $reference) { + // find actionGroupObject + $entity = $this->findEntity($reference); + if ($entity !== null) { + $allEntities[$entity->getName()] = $entity; + } + } + foreach ($extendReferences[1] as $reference) { + // find extended object + $entity = $this->findEntity($reference); + if ($entity !== null) { + $allEntities[$entity->getName()] = $entity; + } + } + + $modulesReferencedInTest = $this->getModuleDependenciesFromReferences($allEntities); + unset($modulesReferencedInTest[$this->moduleNameToComposerName[$moduleFullName]]); + $moduleDependencies = $this->flattenedDependencies[$moduleFullName]; + $diff = array_intersect_key($modulesReferencedInTest, $moduleDependencies); + + if (count($diff) != count($modulesReferencedInTest)) { + $missingDependencies = []; + foreach ($modulesReferencedInTest as $module => $key) { + if (!array_key_exists($module, $diff)) { + $missingDependencies[] = $module; + } + } + $referenceErrors = $this->matchReferencesToMissingDependecies($allEntities, $missingDependencies); + // Build error output + $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\n contains references to following modules:\n\t\t"; + foreach ($missingDependencies as $missingDependency) { + $errorOutput .= "\n\t{$missingDependency}"; + foreach ($referenceErrors[$missingDependency] as $entityName => $filename) { + $errorOutput .= "\n\t\t {$entityName} from {$filename}"; + } + } + $testErrors[$filePath->getRealPath()][] = $errorOutput; + } + } + return $testErrors; + } + + + /** + * Builds and returns array of FullModuleNae => composer name + * @param array $array + * @return array + */ + private function buildModuleNameToComposerName($array) + { + $returnList = []; + foreach ($array as $moduleName => $path) { + $composerData = json_decode(file_get_contents($path . DIRECTORY_SEPARATOR . "composer.json")); + $returnList[$moduleName] = $composerData->name; + } + return $returnList; + } + + /** + * Builds and returns flattened dependency list based on composer dependencies + * @return array + */ + private function buildComposerDependencyList() + { + $flattenedDependencies = []; + + foreach ($this->moduleNameToPath as $moduleName => $pathToModule) { + $composerData = json_decode(file_get_contents($pathToModule . DIRECTORY_SEPARATOR . "composer.json"), true); + $this->allDependencies[$moduleName] = $composerData['require']; + } + foreach ($this->allDependencies as $moduleName => $dependencies) { + $this->alreadyExtractedDependencies = []; + $flattenedDependencies[$moduleName] = $this->extractSubDependencies($moduleName); + } + return $flattenedDependencies; + } + + /** + * Recursive function to fetch dependencies of given dependency, and its child dependencies + * @param string $subDependencyName + * @return array + */ + private function extractSubDependencies($subDependencyName) + { + $flattenedArray = []; - // Check actionGroup references + if (array_search($subDependencyName, $this->alreadyExtractedDependencies) !== false) { + return $flattenedArray; + } + if (isset($this->allDependencies[$subDependencyName])) { + $subDependencyArray = $this->allDependencies[$subDependencyName]; + $flattenedArray = array_merge($flattenedArray, $this->allDependencies[$subDependencyName]); + + // Keep track of dependencies that have already been used, prevents circular dependency problems + $this->alreadyExtractedDependencies[] = $subDependencyName; + foreach ($subDependencyArray as $composerDependencyName => $version) { + $subDependencyFullName = array_search($composerDependencyName, $this->moduleNameToComposerName); + $flattenedArray = array_merge( + $flattenedArray, + $this->extractSubDependencies($subDependencyFullName) + ); + } + } + return $flattenedArray; + } + + /** + * Finds unique array ofcomposer dependencies of given testObjects + * @param array $array + * @return array + */ + private function getModuleDependenciesFromReferences($array) + { + $filenames = []; + foreach ($array as $item) { + // Should it append ALL filenames, including merges? + $allFiles = explode(",", $item->getFilename()); + $basefile = $allFiles[0]; + $modulePath = dirname(dirname(dirname(dirname($basefile)))); + $fullModuleName = array_search($modulePath, $this->moduleNameToPath); + $composerModuleName = $this->moduleNameToComposerName[$fullModuleName]; + $filenames[$composerModuleName] = $composerModuleName; + } + return $filenames; + } + + /** + * Matches references given to list of missing dependencies, returning array of ReferenceName => filename + * @param array $allReferences + * @param array $missingDependencies + * @return array + */ + private function matchReferencesToMissingDependecies($allReferences, $missingDependencies) + { + $referenceErrors = []; + foreach ($allReferences as $reference) { + $allFiles = explode(",", $reference->getFilename()); + $basefile = $allFiles[0]; + $modulePath = dirname(dirname(dirname(dirname($basefile)))); + $fullModuleName = array_search($modulePath, $this->moduleNameToPath); + $composerModuleName = $this->moduleNameToComposerName[$fullModuleName]; + if (array_search($composerModuleName, $missingDependencies) !== false) { + $referenceErrors[$composerModuleName][$reference->getName()] = $basefile; + } + } + return $referenceErrors; + } + + private function buildFileList($modulePaths, $path) + { + $filesAggregate = []; + $finder = new Finder(); + foreach ($modulePaths as $modulePath) { + if (!realpath($modulePath . $path)) { + continue; + } + $finder->files()->in($modulePath . $path)->name("*.xml"); + foreach ($finder->files() as $file) { + $filesAggregate[] = $file->getFilename(); + } + } + return $finder->files(); + } + + private function findEntity($name) + { + if ($name == '_ENV' || $name == '_CREDS') { + return null; + } + + if (DataObjectHandler::getInstance()->getObject($name)) { + return DataObjectHandler::getInstance()->getObject($name); + } elseif (PageObjectHandler::getInstance()->getObject($name)) { + return PageObjectHandler::getInstance()->getObject($name); + } elseif (SectionObjectHandler::getInstance()->getObject($name)) { + return SectionObjectHandler::getInstance()->getObject($name); + } + + try { + return ActionGroupObjectHandler::getInstance()->getObject($name); + } catch (TestReferenceException $e) { + } + try { + return TestObjectHandler::getInstance()->getObject($name); + } catch (TestReferenceException $e) { + } + return null; + } + + + + + + /** + * Prints out given errors to file, and returns summary result string + * @param array $errors + * @return string + */ + private function printErrorsToFile($errors) + { + if (empty($errors)) { + return "No Test Dependency errors found."; + } + $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-dependency-checks.txt"; + $fileResource = fopen($outputPath, 'w'); + $header = "MFTF Test Dependency Check:\n"; + fwrite($fileResource, $header); + foreach ($errors as $test => $error) { + fwrite($fileResource, $error[0] . PHP_EOL); + } + fclose($fileResource); + $errorCount = count($errors); + $output = "Test Dependency errors found across {$errorCount} test(s). Error details output to {$outputPath}"; + return $output; } } From 1f10fc45b0fe0b546745a655c94ac826bae35db5 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 27 Feb 2019 11:16:57 -0600 Subject: [PATCH 06/10] MQE-810: Create a static test to validate references between modules - fixed actionGroup argument false flagging - reafactored dependencies check to validate on all entity file origins --- .../StaticCheck/TestDependencyCheck.php | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 4f09cbb97..760f90fb8 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -28,6 +28,7 @@ class TestDependencyCheck implements StaticCheckInterface { const EXTENDS_REGEX_PATTERN = '/extends=["\']([^\'"]*)/'; const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/'; + const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/'; /** @@ -140,7 +141,12 @@ private function findErrorsInFileSet($files) foreach ($braceReferences[0] as $reference) { // trim `{{data.field}}` to `data` preg_match('/{{([^.]+)/', $reference, $entityName); + // Double check that {{data.field}} isn't an argument for an ActionGroup $entity = $this->findEntity($entityName[1]); + preg_match_all(self::ACTIONGROUP_ARGUMENT_REGEX_PATTERN, $contents, $possibleArgument); + if (array_search($entityName[1], $possibleArgument[1]) !== false) { + continue; + } if ($entity !== null) { $allEntities[$entity->getName()] = $entity; } @@ -160,26 +166,29 @@ private function findErrorsInFileSet($files) } } - $modulesReferencedInTest = $this->getModuleDependenciesFromReferences($allEntities); - unset($modulesReferencedInTest[$this->moduleNameToComposerName[$moduleFullName]]); + $currentModule = $this->moduleNameToComposerName[$moduleFullName]; + $modulesReferencedInTest = $this->getModuleDependenciesFromReferences($allEntities, $currentModule); $moduleDependencies = $this->flattenedDependencies[$moduleFullName]; - $diff = array_intersect_key($modulesReferencedInTest, $moduleDependencies); - - if (count($diff) != count($modulesReferencedInTest)) { - $missingDependencies = []; - foreach ($modulesReferencedInTest as $module => $key) { - if (!array_key_exists($module, $diff)) { - $missingDependencies[] = $module; + // Find Violations + $violatingReferences = []; + foreach ($modulesReferencedInTest as $entityName => $files) { + $valid = false; + foreach ($files as $module) { + if (array_key_exists($module, $moduleDependencies) || $module == $currentModule) { + $valid = true; + break; } } - $referenceErrors = $this->matchReferencesToMissingDependecies($allEntities, $missingDependencies); + if (!$valid) { + $violatingReferences[$entityName] = $files; + } + } + + if (!empty($violatingReferences)) { // Build error output $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\n contains references to following modules:\n\t\t"; - foreach ($missingDependencies as $missingDependency) { - $errorOutput .= "\n\t{$missingDependency}"; - foreach ($referenceErrors[$missingDependency] as $entityName => $filename) { - $errorOutput .= "\n\t\t {$entityName} from {$filename}"; - } + foreach ($violatingReferences as $entityName => $files) { + $errorOutput .= "\n\t {$entityName} in modules: " . implode(", ", $files); } $testErrors[$filePath->getRealPath()][] = $errorOutput; } @@ -263,11 +272,12 @@ private function getModuleDependenciesFromReferences($array) foreach ($array as $item) { // Should it append ALL filenames, including merges? $allFiles = explode(",", $item->getFilename()); - $basefile = $allFiles[0]; - $modulePath = dirname(dirname(dirname(dirname($basefile)))); - $fullModuleName = array_search($modulePath, $this->moduleNameToPath); - $composerModuleName = $this->moduleNameToComposerName[$fullModuleName]; - $filenames[$composerModuleName] = $composerModuleName; + foreach ($allFiles as $file) { + $modulePath = dirname(dirname(dirname(dirname($file)))); + $fullModuleName = array_search($modulePath, $this->moduleNameToPath); + $composerModuleName = $this->moduleNameToComposerName[$fullModuleName]; + $filenames[$item->getName()][] = $composerModuleName; + } } return $filenames; } From 56046d6de6c12766e60ae04f9b204bc1e1ad4c77 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 27 Feb 2019 15:25:18 -0600 Subject: [PATCH 07/10] MQE-810: Create a static test to validate references between modules - Fixed not all directories being parsed - Added check for parameters of elements/urls --- .../StaticCheck/TestDependencyCheck.php | 87 ++++++++----------- .../Test/Objects/ActionObject.php | 8 +- .../Util/TestGenerator.php | 3 +- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 760f90fb8..c36d5f777 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -16,6 +16,7 @@ use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Util\ModuleResolver; +use Magento\FunctionalTestingFramework\Util\TestGenerator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; @@ -30,7 +31,6 @@ class TestDependencyCheck implements StaticCheckInterface const ACTIONGROUP_REGEX_PATTERN = '/ref=["\']([^\'"]*)/'; const ACTIONGROUP_ARGUMENT_REGEX_PATTERN = '/]*name="([^"\']*)/'; - /** * Array of FullModuleName => [dependencies] * @var array @@ -86,18 +86,7 @@ public function execute(InputInterface $input) $this->moduleNameToComposerName = $this->buildModuleNameToComposerName($this->moduleNameToPath); $this->flattenedDependencies = $this->buildComposerDependencyList(); - $allModules = []; - - // Trim non-magento modules from search pool. - foreach (ModuleResolver::getInstance()->getModulesPath() as $module){ - $tempModule = rtrim($module, '/Test/Mftf'); - if (array_search($tempModule, $this->moduleNameToPath)) { - $allModules[] = $module; - } - } - - $testErrors = []; - + $allModules = ModuleResolver::getInstance()->getModulesPath(); $filePaths = [ DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR . 'ActionGroup' . DIRECTORY_SEPARATOR, @@ -108,6 +97,7 @@ public function execute(InputInterface $input) $actionGroupXmlFiles = $this->buildFileList($allModules, $filePaths[1]); $dataXmlFiles= $this->buildFileList($allModules, $filePaths[2]); + $testErrors = []; $testErrors += $this->findErrorsInFileSet($testXmlFiles); $testErrors += $this->findErrorsInFileSet($actionGroupXmlFiles); $testErrors += $this->findErrorsInFileSet($dataXmlFiles); @@ -137,7 +127,9 @@ private function findErrorsInFileSet($files) $braceReferences[0] = array_unique($braceReferences[0]); $actionGroupReferences[1] = array_unique($actionGroupReferences[1]); $braceReferences[1] = array_unique($braceReferences[1]); + $braceReferences[2] = array_filter(array_unique($braceReferences[2])); + // Check `data` entities in {{data.field}} or {{data.field('param')}} foreach ($braceReferences[0] as $reference) { // trim `{{data.field}}` to `data` preg_match('/{{([^.]+)/', $reference, $entityName); @@ -151,15 +143,40 @@ private function findErrorsInFileSet($files) $allEntities[$entity->getName()] = $entity; } } + + // Drill down into params in {{ref.params('string', $data.key$, entity.reference)}} + foreach ($braceReferences[2] as $parameterizedReference) { + preg_match(ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER, $parameterizedReference, $arguments); + $splitArguments = explode(',', ltrim(rtrim($arguments[0], ")"), "(")); + foreach ($splitArguments as $argument) { + // Do nothing for 'string' or $persisted.data$ + if (preg_match(ActionObject::STRING_PARAMETER_REGEX, $argument)) { + continue; + } elseif (preg_match(TestGenerator::PERSISTED_OBJECT_NOTATION_REGEX, $argument)) { + continue; + } + // trim `data.field` to `data` + preg_match('/([^.]+)/', $argument, $entityName); + // Double check that {{data.field}} isn't an argument for an ActionGroup + $entity = $this->findEntity($entityName[1]); + preg_match_all(self::ACTIONGROUP_ARGUMENT_REGEX_PATTERN, $contents, $possibleArgument); + if (array_search($entityName[1], $possibleArgument[1]) !== false) { + continue; + } + if ($entity !== null) { + $allEntities[$entity->getName()] = $entity; + } + } + } + // Check actionGroup references foreach ($actionGroupReferences[1] as $reference) { - // find actionGroupObject $entity = $this->findEntity($reference); if ($entity !== null) { $allEntities[$entity->getName()] = $entity; } } + // Check extended objects foreach ($extendReferences[1] as $reference) { - // find extended object $entity = $this->findEntity($reference); if ($entity !== null) { $allEntities[$entity->getName()] = $entity; @@ -186,9 +203,9 @@ private function findErrorsInFileSet($files) if (!empty($violatingReferences)) { // Build error output - $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\n contains references to following modules:\n\t\t"; + $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\ncontains references to following modules:\n\t\t"; foreach ($violatingReferences as $entityName => $files) { - $errorOutput .= "\n\t {$entityName} in modules: " . implode(", ", $files); + $errorOutput .= "\n\t {$entityName} from modules: " . implode(", ", $files); } $testErrors[$filePath->getRealPath()][] = $errorOutput; } @@ -282,40 +299,14 @@ private function getModuleDependenciesFromReferences($array) return $filenames; } - /** - * Matches references given to list of missing dependencies, returning array of ReferenceName => filename - * @param array $allReferences - * @param array $missingDependencies - * @return array - */ - private function matchReferencesToMissingDependecies($allReferences, $missingDependencies) - { - $referenceErrors = []; - foreach ($allReferences as $reference) { - $allFiles = explode(",", $reference->getFilename()); - $basefile = $allFiles[0]; - $modulePath = dirname(dirname(dirname(dirname($basefile)))); - $fullModuleName = array_search($modulePath, $this->moduleNameToPath); - $composerModuleName = $this->moduleNameToComposerName[$fullModuleName]; - if (array_search($composerModuleName, $missingDependencies) !== false) { - $referenceErrors[$composerModuleName][$reference->getName()] = $basefile; - } - } - return $referenceErrors; - } - private function buildFileList($modulePaths, $path) { - $filesAggregate = []; $finder = new Finder(); foreach ($modulePaths as $modulePath) { if (!realpath($modulePath . $path)) { continue; } $finder->files()->in($modulePath . $path)->name("*.xml"); - foreach ($finder->files() as $file) { - $filesAggregate[] = $file->getFilename(); - } } return $finder->files(); } @@ -345,10 +336,6 @@ private function findEntity($name) return null; } - - - - /** * Prints out given errors to file, and returns summary result string * @param array $errors @@ -357,18 +344,18 @@ private function findEntity($name) private function printErrorsToFile($errors) { if (empty($errors)) { - return "No Test Dependency errors found."; + return "No Dependency errors found."; } $outputPath = getcwd() . DIRECTORY_SEPARATOR . "mftf-dependency-checks.txt"; $fileResource = fopen($outputPath, 'w'); - $header = "MFTF Test Dependency Check:\n"; + $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 = "Test Dependency errors found across {$errorCount} test(s). Error details output to {$outputPath}"; + $output = "Dependency errors found across {$errorCount} file(s). Error details output to {$outputPath}"; return $output; } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index 166a61889..5205d69cf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -68,6 +68,7 @@ class ActionObject const ACTION_ATTRIBUTE_SELECTOR = 'selector'; const ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER = '/\(.+\)/'; const ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN = '/({{[\w]+\.[\w\[\]]+}})|({{[\w]+\.[\w]+\((?(?!}}).)+\)}})/'; + const STRING_PARAMETER_REGEX = "/'[^']+'/"; const DEFAULT_WAIT_TIMEOUT = 10; /** @@ -464,8 +465,6 @@ private function stripAndSplitReference($reference) */ private function stripAndReturnParameters($reference) { - // 'string', or 'string,!@#$%^&*()_+, ' - $literalParametersRegex = "/'[^']+'/"; $postCleanupDelimiter = "::::"; preg_match(ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER, $reference, $matches); @@ -473,8 +472,9 @@ private function stripAndReturnParameters($reference) $strippedReference = ltrim(rtrim($matches[0], ")"), "("); // Pull out all 'string' references, as they can contain 'string with , comma in it' - preg_match_all($literalParametersRegex, $strippedReference, $literalReferences); - $strippedReference = preg_replace($literalParametersRegex, '&&stringReference&&', $strippedReference); + // 'string', or 'string,!@#$%^&*()_+, ' + preg_match_all(self::STRING_PARAMETER_REGEX, $strippedReference, $literalReferences); + $strippedReference = preg_replace(self::STRING_PARAMETER_REGEX, '&&stringReference&&', $strippedReference); // Sanitize 'string, data.field,$persisted.field$' => 'string::::data.field::::$persisted.field$' $strippedReference = preg_replace('/,/', ', ', $strippedReference); diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index abc62eb20..07cb96b3a 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -38,6 +38,7 @@ class TestGenerator const HOOK_SCOPE = 'hook'; const SUITE_SCOPE = 'suite'; const PRESSKEY_ARRAY_ANCHOR_KEY = '987654321098765432109876543210'; + const PERSISTED_OBJECT_NOTATION_REGEX = '/\${1,2}[\w.\[\]]+\${1,2}/'; /** * Path to the export dir. @@ -1332,7 +1333,7 @@ private function resolveTestVariable($args, $actionOrigin) } $outputArg = $arg; // Math on $data.key$ and $$data.key$$ - preg_match_all('/\${1,2}[\w.\[\]]+\${1,2}/', $outputArg, $matches); + preg_match_all(self::PERSISTED_OBJECT_NOTATION_REGEX, $outputArg, $matches); $this->replaceMatchesIntoArg($matches[0], $outputArg); //trim "{$variable}" into $variable From 64f82cf4f9a7a4c6088127b8a7f1c30472d00860 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Thu, 28 Feb 2019 09:09:41 -0600 Subject: [PATCH 08/10] MQE-810: Create a static test to validate references between modules - Static/Unit test Fixes --- .../Objects/EntityDataObject.php | 12 ++++++-- .../Page/Handlers/SectionObjectHandler.php | 2 +- .../Page/Objects/PageObject.php | 2 +- .../Page/Objects/SectionObject.php | 2 +- .../StaticCheck/TestDependencyCheck.php | 28 +++++++++++++++++-- .../Test/Objects/ActionGroupObject.php | 2 +- 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index 7123b26d4..070a08bf1 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -90,8 +90,16 @@ class EntityDataObject * @param string $parentEntity * @param string $filename */ - public function __construct($name, $type, $data, $linkedEntities, $uniquenessData, $vars = [], $parentEntity = null, $filename) - { + public function __construct( + $name, + $type, + $data, + $linkedEntities, + $uniquenessData, + $vars = [], + $parentEntity = null, + $filename = null + ) { $this->name = $name; $this->type = $type; $this->data = $data; diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php index 61aaf3f08..e77e4c502 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php @@ -87,7 +87,7 @@ private function __construct() throw new XmlException($exception->getMessage() . " in Section '{$sectionName}'"); } - $filename = $sectionData[self::FILENAME]; + $filename = $sectionData[self::FILENAME] ?? null; $this->sectionObjects[$sectionName] = new SectionObject($sectionName, $elements, $filename); } } diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php index 91bc241cb..d99c40240 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/PageObject.php @@ -75,7 +75,7 @@ class PageObject * @param string $area * @param string $filename */ - public function __construct($name, $url, $module, $sections, $parameterized, $area, $filename) + public function __construct($name, $url, $module, $sections, $parameterized, $area, $filename = null) { $this->name = $name; $this->url = $url; diff --git a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php index aae339417..8c1cac326 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php +++ b/src/Magento/FunctionalTestingFramework/Page/Objects/SectionObject.php @@ -38,7 +38,7 @@ class SectionObject * @param array $elements * @param string $filename */ - public function __construct($name, $elements, $filename) + public function __construct($name, $elements, $filename = null) { $this->name = $name; $this->elements = $elements; diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index c36d5f777..9515084a7 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -24,6 +24,7 @@ /** * Class TestDependencyCheck * @package Magento\FunctionalTestingFramework\StaticCheck + * @SuppressWarnings(PHPMD) */ class TestDependencyCheck implements StaticCheckInterface { @@ -106,6 +107,13 @@ public function execute(InputInterface $input) return $this->printErrorsToFile($testErrors); } + /** + * Finds all reference errors in given set of files + * @param Finder $files + * @return array + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException + */ private function findErrorsInFileSet($files) { $testErrors = []; @@ -146,7 +154,11 @@ private function findErrorsInFileSet($files) // Drill down into params in {{ref.params('string', $data.key$, entity.reference)}} foreach ($braceReferences[2] as $parameterizedReference) { - preg_match(ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER, $parameterizedReference, $arguments); + preg_match( + ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PARAMETER, + $parameterizedReference, + $arguments + ); $splitArguments = explode(',', ltrim(rtrim($arguments[0], ")"), "(")); foreach ($splitArguments as $argument) { // Do nothing for 'string' or $persisted.data$ @@ -213,7 +225,6 @@ private function findErrorsInFileSet($files) return $testErrors; } - /** * Builds and returns array of FullModuleNae => composer name * @param array $array @@ -299,6 +310,12 @@ 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(); @@ -311,6 +328,13 @@ private function buildFileList($modulePaths, $path) return $finder->files(); } + /** + * Attempts to find any MFTF entity by its name. Returns null if none are found. + * @param string $name + * @return mixed + * @throws \Magento\FunctionalTestingFramework\Exceptions\TestReferenceException + * @throws \Magento\FunctionalTestingFramework\Exceptions\XmlException + */ private function findEntity($name) { if ($name == '_ENV' || $name == '_CREDS') { diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index 485b8cb9c..2bed003e5 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -87,7 +87,7 @@ class ActionGroupObject * @param string $parentActionGroup * @param string $filename */ - public function __construct($name, $arguments, $actions, $parentActionGroup, $filename) + public function __construct($name, $arguments, $actions, $parentActionGroup, $filename = null) { $this->varAttributes = array_merge( ActionObject::SELECTOR_ENABLED_ATTRIBUTES, From 334416c8f81b27ee8f85830c7c6a15b5e063c570 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 27 Mar 2019 08:37:31 -0500 Subject: [PATCH 09/10] MQE-810: Create a static test to validate references between modules - Updated Output error message --- .../StaticCheck/TestDependencyCheck.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index 9515084a7..be9ab582f 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -215,9 +215,9 @@ private function findErrorsInFileSet($files) if (!empty($violatingReferences)) { // Build error output - $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\ncontains references to following modules:\n\t\t"; + $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\ncontains entity references that violate dependency constraints:\n\t\t"; foreach ($violatingReferences as $entityName => $files) { - $errorOutput .= "\n\t {$entityName} from modules: " . implode(", ", $files); + $errorOutput .= "\n\t {$entityName} from module(s): " . implode(", ", $files); } $testErrors[$filePath->getRealPath()][] = $errorOutput; } From e5c98796ca83803b3c38294b44abf9f3e9ae8c6a Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Wed, 27 Mar 2019 14:03:24 -0500 Subject: [PATCH 10/10] MQE-810: Create a static test to validate references between modules - Fixed Static Check --- .../StaticCheck/TestDependencyCheck.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php index be9ab582f..e0b3cefd6 100644 --- a/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php +++ b/src/Magento/FunctionalTestingFramework/StaticCheck/TestDependencyCheck.php @@ -215,7 +215,8 @@ private function findErrorsInFileSet($files) if (!empty($violatingReferences)) { // Build error output - $errorOutput = "\nFile \"{$filePath->getRealPath()}\"\ncontains entity references that violate dependency constraints:\n\t\t"; + $errorOutput = "\nFile \"{$filePath->getRealPath()}\""; + $errorOutput .= "\ncontains entity references that violate dependency constraints:\n\t\t"; foreach ($violatingReferences as $entityName => $files) { $errorOutput .= "\n\t {$entityName} from module(s): " . implode(", ", $files); }