diff --git a/composer.json b/composer.json
index b33567303..e7ee72ff1 100755
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
"ext-curl": "*",
"allure-framework/allure-codeception": "~1.3.0",
"codeception/codeception": "~2.3.4 || ~2.4.0 ",
+ "composer/composer": "^1.6",
"consolidation/robo": "^1.0.0",
"csharpru/vault-php": "~3.5.3",
"csharpru/vault-php-guzzle6-transport": "^2.0",
diff --git a/composer.lock b/composer.lock
index dd7d6ad3f..28cbd1d4e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "01cbd9e237e76de7070a3c0de4ee8b9f",
+ "content-hash": "79e0a6006597df1c5511869876dd7777",
"packages": [
{
"name": "allure-framework/allure-codeception",
@@ -250,7 +250,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
- "homepage": "https://github.com/Nyholm"
+ "homepage": "https://github.com/nyholm"
}
],
"description": "Library of all the php-cache adapters",
@@ -388,6 +388,308 @@
"description": "Flexible Stub wrapper for PHPUnit's Mock Builder",
"time": "2018-05-17T09:31:08+00:00"
},
+ {
+ "name": "composer/ca-bundle",
+ "version": "1.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/ca-bundle.git",
+ "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
+ "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "ext-pcre": "*",
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
+ "psr/log": "^1.0",
+ "symfony/process": "^2.5 || ^3.0 || ^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\CaBundle\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
+ "keywords": [
+ "cabundle",
+ "cacert",
+ "certificate",
+ "ssl",
+ "tls"
+ ],
+ "time": "2019-08-30T08:44:50+00:00"
+ },
+ {
+ "name": "composer/composer",
+ "version": "1.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/composer.git",
+ "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
+ "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
+ "shasum": ""
+ },
+ "require": {
+ "composer/ca-bundle": "^1.0",
+ "composer/semver": "^1.0",
+ "composer/spdx-licenses": "^1.2",
+ "composer/xdebug-handler": "^1.1",
+ "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
+ "php": "^5.3.2 || ^7.0",
+ "psr/log": "^1.0",
+ "seld/jsonlint": "^1.4",
+ "seld/phar-utils": "^1.0",
+ "symfony/console": "^2.7 || ^3.0 || ^4.0",
+ "symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
+ "symfony/finder": "^2.7 || ^3.0 || ^4.0",
+ "symfony/process": "^2.7 || ^3.0 || ^4.0"
+ },
+ "conflict": {
+ "symfony/console": "2.8.38"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7",
+ "phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
+ },
+ "suggest": {
+ "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
+ "ext-zip": "Enabling the zip extension allows you to unzip archives",
+ "ext-zlib": "Allow gzip compression of HTTP requests"
+ },
+ "bin": [
+ "bin/composer"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\": "src/Composer"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
+ "homepage": "https://getcomposer.org/",
+ "keywords": [
+ "autoload",
+ "dependency",
+ "package"
+ ],
+ "time": "2019-08-02T18:55:33+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
+ "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.5 || ^5.0.5",
+ "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "time": "2019-03-19T17:25:45+00:00"
+ },
+ {
+ "name": "composer/spdx-licenses",
+ "version": "1.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/spdx-licenses.git",
+ "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5",
+ "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Spdx\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "SPDX licenses list and validation library.",
+ "keywords": [
+ "license",
+ "spdx",
+ "validator"
+ ],
+ "time": "2019-07-29T10:31:59+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "1.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
+ "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0",
+ "psr/log": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "time": "2019-05-27T17:52:04+00:00"
+ },
{
"name": "consolidation/annotated-command",
"version": "2.9.1",
@@ -1859,6 +2161,72 @@
],
"time": "2019-04-17T08:12:16+00:00"
},
+ {
+ "name": "justinrainbow/json-schema",
+ "version": "5.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/justinrainbow/json-schema.git",
+ "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
+ "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2.2.20",
+ "json-schema/json-schema-test-suite": "1.2.0",
+ "phpunit/phpunit": "^4.8.35"
+ },
+ "bin": [
+ "bin/validate-json"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JsonSchema\\": "src/JsonSchema/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bruno Prieto Reis",
+ "email": "bruno.p.reis@gmail.com"
+ },
+ {
+ "name": "Justin Rainbow",
+ "email": "justin.rainbow@gmail.com"
+ },
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ },
+ {
+ "name": "Robert Schönthal",
+ "email": "seroscho@googlemail.com"
+ }
+ ],
+ "description": "A library to validate a json schema.",
+ "homepage": "https://github.com/justinrainbow/json-schema",
+ "keywords": [
+ "json",
+ "schema"
+ ],
+ "time": "2019-01-14T23:55:14+00:00"
+ },
{
"name": "league/container",
"version": "2.4.1",
@@ -3960,6 +4328,99 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03T07:35:21+00:00"
},
+ {
+ "name": "seld/jsonlint",
+ "version": "1.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/jsonlint.git",
+ "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38",
+ "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+ },
+ "bin": [
+ "bin/jsonlint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Seld\\JsonLint\\": "src/Seld/JsonLint/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "JSON Linter",
+ "keywords": [
+ "json",
+ "linter",
+ "parser",
+ "validator"
+ ],
+ "time": "2018-01-24T12:46:19+00:00"
+ },
+ {
+ "name": "seld/phar-utils",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/phar-utils.git",
+ "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a",
+ "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Seld\\PharUtils\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be"
+ }
+ ],
+ "description": "PHAR file format utilities, for when PHP phars you up",
+ "keywords": [
+ "phra"
+ ],
+ "time": "2015-10-13T18:44:15+00:00"
+ },
{
"name": "symfony/browser-kit",
"version": "v3.4.18",
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php
index 99125a9e3..5bf29507c 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/Handlers/SuiteObjectHandlerTest.php
@@ -15,9 +15,16 @@
use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
use tests\unit\Util\SuiteDataArrayBuilder;
use tests\unit\Util\TestDataArrayBuilder;
+use tests\unit\Util\MockModuleResolverBuilder;
class SuiteObjectHandlerTest extends MagentoTestCase
{
+ public function setUp()
+ {
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
+ }
+
/**
* Tests basic parsing and accesors of suite object and suite object supporting classes
*/
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php
index eb6298afc..29158b0f5 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php
@@ -20,10 +20,10 @@
use tests\unit\Util\SuiteDataArrayBuilder;
use tests\unit\Util\TestDataArrayBuilder;
use tests\unit\Util\TestLoggingUtil;
+use tests\unit\Util\MockModuleResolverBuilder;
class SuiteGeneratorTest extends MagentoTestCase
{
-
/**
* Setup entry append and clear for Suite Generator
*/
@@ -42,6 +42,8 @@ public static function setUpBeforeClass()
public function setUp()
{
TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
}
/**
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
index a94c41ae4..dd0dddc1c 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Handlers/TestObjectHandlerTest.php
@@ -8,7 +8,6 @@
use AspectMock\Test as AspectMock;
-use Go\Aop\Aspect;
use Magento\FunctionalTestingFramework\ObjectManager;
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler;
@@ -16,10 +15,10 @@
use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject;
use Magento\FunctionalTestingFramework\Test\Objects\TestObject;
use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser;
-use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor;
use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor;
use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
use tests\unit\Util\TestDataArrayBuilder;
+use tests\unit\Util\MockModuleResolverBuilder;
class TestObjectHandlerTest extends MagentoTestCase
{
@@ -40,10 +39,13 @@ public function testGetTestObject()
->withTestActions()
->build();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
$this->setMockParserOutput(['tests' => $mockData]);
// run object handler method
$toh = TestObjectHandler::getInstance();
+ $mockConfig = AspectMock::double(TestObjectHandler::class, ['initTestData' => false]);
$actualTestObject = $toh->getObject($testDataArrayBuilder->testName);
// perform asserts
@@ -130,6 +132,8 @@ public function testGetTestsByGroup()
->withTestActions()
->build();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
$this->setMockParserOutput(['tests' => array_merge($includeTest, $excludeTest)]);
// execute test method
@@ -150,16 +154,20 @@ public function testGetTestsByGroup()
public function testGetTestWithModuleName()
{
// set up Test Data
- $moduleExpected = "SomeTestModule";
+ $moduleExpected = "SomeModuleName";
+ $moduleExpectedTest = $moduleExpected . "Test";
$filepath = DIRECTORY_SEPARATOR .
- "user" .
+ "user" . DIRECTORY_SEPARATOR .
"magento2ce" . DIRECTORY_SEPARATOR .
"dev" . DIRECTORY_SEPARATOR .
"tests" . DIRECTORY_SEPARATOR .
"acceptance" . DIRECTORY_SEPARATOR .
"tests" . DIRECTORY_SEPARATOR .
- $moduleExpected . DIRECTORY_SEPARATOR .
- "Tests" . DIRECTORY_SEPARATOR .
+ "functional" . DIRECTORY_SEPARATOR .
+ "Vendor" . DIRECTORY_SEPARATOR .
+ $moduleExpectedTest;
+ $file = $filepath . DIRECTORY_SEPARATOR .
+ "Test" . DIRECTORY_SEPARATOR .
"text.xml";
// set up mock data
$testDataArrayBuilder = new TestDataArrayBuilder();
@@ -169,8 +177,12 @@ public function testGetTestWithModuleName()
->withAfterHook()
->withBeforeHook()
->withTestActions()
- ->withFileName($filepath)
+ ->withFileName($file)
->build();
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup(['Vendor_' . $moduleExpected => $filepath]);
+
$this->setMockParserOutput(['tests' => $mockData]);
// Execute Test Method
$toh = TestObjectHandler::getInstance();
@@ -197,6 +209,8 @@ public function testGetTestObjectWithInvalidExtends()
->withBeforeHook()
->withTestActions()
->build();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
$this->setMockParserOutput(['tests' => $testOne]);
$toh = TestObjectHandler::getInstance();
@@ -233,6 +247,8 @@ public function testGetAllTestObjectsWithInvalidExtends()
->withTestActions()
->build();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
$this->setMockParserOutput(['tests' => array_merge($testOne, $testTwo)]);
$toh = TestObjectHandler::getInstance();
@@ -260,4 +276,14 @@ private function setMockParserOutput($data)
->make(); // bypass the private constructor
AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
}
+
+ /**
+ * After method functionality
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ AspectMock::clean();
+ }
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php
index ce184e564..d86185b83 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php
@@ -20,6 +20,7 @@
use PHPUnit\Framework\TestCase;
use tests\unit\Util\TestDataArrayBuilder;
use tests\unit\Util\TestLoggingUtil;
+use tests\unit\Util\MockModuleResolverBuilder;
class ObjectExtensionUtilTest extends TestCase
{
@@ -30,6 +31,8 @@ class ObjectExtensionUtilTest extends TestCase
public function setUp()
{
TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup();
}
/**
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php
index 5efa6384b..05bc99b89 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModulePathExtractorTest.php
@@ -7,103 +7,128 @@
namespace tests\unit\Magento\FunctionalTestFramework\Test\Util;
use Magento\FunctionalTestingFramework\Util\ModulePathExtractor;
-use PHPUnit\Framework\TestCase;
+use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
+use tests\unit\Util\MockModuleResolverBuilder;
-class ModulePathExtractorTest extends TestCase
+class ModulePathExtractorTest extends MagentoTestCase
{
- const EXTENSION_PATH = "app"
- . DIRECTORY_SEPARATOR
- . "code"
- . DIRECTORY_SEPARATOR
- . "TestExtension"
- . DIRECTORY_SEPARATOR
- . "[Analytics]"
- . DIRECTORY_SEPARATOR
- . "Test"
- . DIRECTORY_SEPARATOR
- . "Mftf"
- . DIRECTORY_SEPARATOR
- . "Test"
- . DIRECTORY_SEPARATOR
- . "SomeText.xml";
-
- const MAGENTO_PATH = "dev"
- . DIRECTORY_SEPARATOR
- . "tests"
- . DIRECTORY_SEPARATOR
- . "acceptance"
- . DIRECTORY_SEPARATOR
- . "tests"
- . DIRECTORY_SEPARATOR
- . "functional"
- . DIRECTORY_SEPARATOR
- . "Magento"
- . DIRECTORY_SEPARATOR
- . "FunctionalTest"
- . DIRECTORY_SEPARATOR
- . "[Analytics]"
- . DIRECTORY_SEPARATOR
- . "Test"
- . DIRECTORY_SEPARATOR
- . "SomeText.xml";
+ /**
+ * Mock test module paths
+ *
+ * @var array
+ */
+ private $mockTestModulePaths = [
+ 'Magento_ModuleA' => '/base/path/app/code/Magento/ModuleA/Test/Mftf',
+ 'VendorB_ModuleB' => '/base/path/app/code/VendorB/ModuleB/Test/Mftf',
+ 'Magento_ModuleC' => '/base/path/dev/tests/acceptance/tests/functional/Magento/ModuleCTest',
+ 'VendorD_ModuleD' => '/base/path/dev/tests/acceptance/tests/functional/VendorD/ModuleDTest',
+ 'SomeModuleE' => '/base/path/dev/tests/acceptance/tests/functional/FunctionalTest/SomeModuleE',
+ 'Magento_ModuleF' => '/base/path/vendor/magento/module-modulef/Test/Mftf',
+ 'VendorG_ModuleG' => '/base/path/vendor/vendorg/module-moduleg-test',
+ ];
+
+ /**
+ * Validate module for app/code path
+ *
+ * @throws \Exception
+ */
+ public function testGetModuleAppCode()
+ {
+ $mockPath = '/base/path/app/code/Magento/ModuleA/Test/Mftf/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('ModuleA', $extractor->extractModuleName($mockPath));
+ }
/**
- * Validate correct module is returned for dev/tests path
+ * Validate vendor for app/code path
+ *
* @throws \Exception
*/
- public function testGetMagentoModule()
+ public function testGetVendorAppCode()
{
- $modulePathExtractor = new ModulePathExtractor();
- $this->assertEquals(
- '[Analytics]',
- $modulePathExtractor->extractModuleName(
- self::MAGENTO_PATH
- )
- );
+ $mockPath = '/base/path/app/code/VendorB/ModuleB/Test/Mftf/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('VendorB', $extractor->getExtensionPath($mockPath));
}
/**
- * Validate correct module is returned for extension path
+ * Validate module for dev/tests path
+ *
* @throws \Exception
*/
- public function testGetExtensionModule()
+ public function testGetModuleDevTests()
{
- $modulePathExtractor = new ModulePathExtractor();
- $this->assertEquals(
- '[Analytics]',
- $modulePathExtractor->extractModuleName(
- self::EXTENSION_PATH
- )
- );
+ $mockPath = '/base/path/dev/tests/acceptance/tests/functional/Magento/ModuleCTest/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('ModuleC', $extractor->extractModuleName($mockPath));
}
/**
- * Validate Magento is returned for dev/tests/acceptance
+ * Validate vendor for dev/tests path
+ *
* @throws \Exception
*/
- public function testMagentoModulePath()
+ public function testGetVendorDevTests()
{
- $modulePathExtractor = new ModulePathExtractor();
- $this->assertEquals(
- 'Magento',
- $modulePathExtractor->getExtensionPath(
- self::MAGENTO_PATH
- )
- );
+ $mockPath = '/base/path/dev/tests/acceptance/tests/functional/VendorD/ModuleDTest/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('VendorD', $extractor->getExtensionPath($mockPath));
}
/**
- * Validate correct extension path is returned
+ * Validate module with no _
+ *
* @throws \Exception
*/
- public function testExtensionModulePath()
+ public function testGetModule()
{
- $modulePathExtractor = new ModulePathExtractor();
- $this->assertEquals(
- 'TestExtension',
- $modulePathExtractor->getExtensionPath(
- self::EXTENSION_PATH
- )
- );
+ $mockPath = '/base/path/dev/tests/acceptance/tests/functional/FunctionalTest/SomeModuleE/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('NO MODULE DETECTED', $extractor->extractModuleName($mockPath));
+ }
+
+ /**
+ * Validate module for vendor/tests path
+ *
+ * @throws \Exception
+ */
+ public function testGetModuleVendorDir()
+ {
+ $mockPath = '/base/path/vendor/magento/module-modulef/Test/Mftf/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('ModuleF', $extractor->extractModuleName($mockPath));
+ }
+
+ /**
+ * Validate vendor for vendor path
+ *
+ * @throws \Exception
+ */
+ public function testGetVendorVendorDir()
+ {
+ $mockPath = '/base/path/vendor/vendorg/module-moduleg-test/Test/SomeTest.xml';
+
+ $resolverMock = new MockModuleResolverBuilder();
+ $resolverMock->setup($this->mockTestModulePaths);
+ $extractor = new ModulePathExtractor();
+ $this->assertEquals('VendorG', $extractor->getExtensionPath($mockPath));
}
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php
index 248704adc..1d4b77740 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php
@@ -56,21 +56,29 @@ public function testGetModulePathsAlreadySet()
*/
public function testGetModulePathsAggregate()
{
+
$this->mockForceGenerate(false);
$this->setMockResolverClass(
false,
null,
null,
null,
- ["Magento_example" => "example" . DIRECTORY_SEPARATOR . "paths"]
+ [
+ 'some' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'example' => ['example'],
+ 'other' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'sample' => ['sample'],
+ ],
+ null,
+ [
+ 'Magento_example' => 'some' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'example',
+ 'Magento_sample' => 'other' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'sample',
+ ]
);
$resolver = ModuleResolver::getInstance();
- $this->setMockResolverProperties($resolver, null, [0 => "Magento_example"]);
+ $this->setMockResolverProperties($resolver, null, [0 => 'Magento_example', 1 => 'Magento_sample']);
$this->assertEquals(
[
- "example" . DIRECTORY_SEPARATOR . "paths",
- "example" . DIRECTORY_SEPARATOR . "paths",
- "example" . DIRECTORY_SEPARATOR . "paths"
+ 'some' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'example',
+ 'other' . DIRECTORY_SEPARATOR . 'path' . DIRECTORY_SEPARATOR . 'sample'
],
$resolver->getModulesPath()
);
@@ -82,22 +90,36 @@ public function testGetModulePathsAggregate()
*/
public function testGetModulePathsLocations()
{
+ // clear test object handler value to inject parsed content
+ $property = new \ReflectionProperty(ModuleResolver::class, 'instance');
+ $property->setAccessible(true);
+ $property->setValue(null);
+
$this->mockForceGenerate(false);
$mockResolver = $this->setMockResolverClass(
true,
- [0 => "example"],
+ [],
+ null,
null,
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
null,
- ["example" => "example" . DIRECTORY_SEPARATOR . "paths"]
+ function ($arg) {
+ return $arg;
+ },
+ function ($arg) {
+ return $arg;
+ }
);
$resolver = ModuleResolver::getInstance();
$this->setMockResolverProperties($resolver, null, null);
$this->assertEquals(
- [
- "example" . DIRECTORY_SEPARATOR . "paths",
- "example" . DIRECTORY_SEPARATOR . "paths",
- "example" . DIRECTORY_SEPARATOR . "paths"
- ],
+ [],
$resolver->getModulesPath()
);
@@ -119,6 +141,20 @@ public function testGetModulePathsLocations()
'Test' . DIRECTORY_SEPARATOR .'Mftf'
]
);
+ $mockResolver->verifyInvoked(
+ 'globRelevantPaths',
+ [
+ $magentoBaseCodePath
+ . DIRECTORY_SEPARATOR . "dev"
+ . DIRECTORY_SEPARATOR . "tests"
+ . DIRECTORY_SEPARATOR . "acceptance"
+ . DIRECTORY_SEPARATOR . "tests"
+ . DIRECTORY_SEPARATOR . "functional"
+ . DIRECTORY_SEPARATOR . "Magento"
+ . DIRECTORY_SEPARATOR . "FunctionalTest"
+ , ''
+ ]
+ );
}
/**
@@ -127,14 +163,21 @@ public function testGetModulePathsLocations()
*/
public function testGetCustomModulePath()
{
- $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, [], ['otherPath']);
+ $this->setMockResolverClass(
+ false,
+ null,
+ null,
+ null,
+ [],
+ ['Magento_Module' => 'otherPath']
+ );
$resolver = ModuleResolver::getInstance();
$this->setMockResolverProperties($resolver, null, null, null);
$this->assertEquals(['otherPath'], $resolver->getModulesPath());
TestLoggingUtil::getInstance()->validateMockLogStatement(
'info',
'including custom module',
- ['module' => 'otherPath']
+ ['Magento_Module' => 'otherPath']
);
}
@@ -149,22 +192,36 @@ public function testGetModulePathsBlacklist()
null,
null,
null,
- function ($arg1, $arg2) {
- if ($arg2 === "") {
- $mockValue = ["somePath" => "somePath"];
- } else {
- $mockValue = ["lastPath" => "lastPath"];
- }
- return $mockValue;
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [
+ "vendor" => "vendor",
+ "appCode" => "appCode",
+ "devTests" => "devTests",
+ "thisPath" => "thisPath"
+ ],
+ function ($arg) {
+ return $arg;
+ },
+ function ($arg) {
+ return $arg;
}
);
$resolver = ModuleResolver::getInstance();
- $this->setMockResolverProperties($resolver, null, null, ["somePath"]);
- $this->assertEquals(["lastPath", "lastPath"], $resolver->getModulesPath());
+ $this->setMockResolverProperties($resolver, null, null, ["devTests" => "devTests"]);
+ $this->assertEquals(
+ ["vendor", "appCode", "thisPath"],
+ $resolver->getModulesPath()
+ );
TestLoggingUtil::getInstance()->validateMockLogStatement(
'info',
'excluding module',
- ['module' => 'somePath']
+ ['module' => 'devTests']
);
}
@@ -252,6 +309,14 @@ public function testGetAdminTokenWithBadResponse()
* @param string[] $mockGlob
* @param string[] $mockRelativePaths
* @param string[] $mockCustomModules
+ * @param string[] $mockGetRegisteredModuleList
+ * @param string[] $mockAggregateTestModulePathsFromComposerJson
+ * @param string[] $mockAggregateTestModulePathsFromComposerInstaller
+ * @param string[] $mockGetComposerJsonTestModulePaths
+ * @param string[] $mockGetComposerInstalledTestModulePaths
+ * @param string[] $mockAggregateTestModulePaths
+ * @param string[] $mockNormalizeModuleNames
+ * @param string[] $mockFlipAndFilterModulePathsArray
* @throws \Exception
* @return Verifier ModuleResolver double
*/
@@ -261,7 +326,15 @@ private function setMockResolverClass(
$mockCustomMethods = null,
$mockGlob = null,
$mockRelativePaths = null,
- $mockCustomModules = null
+ $mockCustomModules = null,
+ $mockGetRegisteredModuleList = null,
+ $mockAggregateTestModulePathsFromComposerJson = [],
+ $mockAggregateTestModulePathsFromComposerInstaller = [],
+ $mockGetComposerJsonTestModulePaths = [],
+ $mockGetComposerInstalledTestModulePaths = [],
+ $mockAggregateTestModulePaths = null,
+ $mockNormalizeModuleNames = null,
+ $mockFlipAndFilterModulePathsArray = null
) {
$property = new \ReflectionProperty(ModuleResolver::class, 'instance');
$property->setAccessible(true);
@@ -286,7 +359,23 @@ private function setMockResolverClass(
if (isset($mockCustomModules)) {
$mockMethods['getCustomModulePaths'] = $mockCustomModules;
}
-// $mockMethods['printMagentoVersionInfo'] = null;
+ if (isset($mockGetRegisteredModuleList)) {
+ $mockMethods['getRegisteredModuleList'] = $mockGetRegisteredModuleList;
+ }
+ $mockMethods['aggregateTestModulePathsFromComposerJson'] = $mockAggregateTestModulePathsFromComposerJson ?? [];
+ $mockMethods['aggregateTestModulePathsFromComposerInstaller'] =
+ $mockAggregateTestModulePathsFromComposerInstaller ?? [];
+ $mockMethods['getComposerJsonTestModulePaths'] = $mockGetComposerJsonTestModulePaths ?? [];
+ $mockMethods['getComposerInstalledTestModulePaths'] = $mockGetComposerInstalledTestModulePaths ?? [];
+ if (isset($mockAggregateTestModulePaths)) {
+ $mockMethods['aggregateTestModulePaths'] = $mockAggregateTestModulePaths;
+ }
+ if (isset($mockNormalizeModuleNames)) {
+ $mockMethods['normalizeModuleNames'] = $mockNormalizeModuleNames;
+ }
+ if (isset($mockFlipAndFilterModulePathsArray)) {
+ $mockMethods['flipAndFilterModulePathsArray'] = $mockFlipAndFilterModulePathsArray;
+ }
$mockResolver = AspectMock::double(
ModuleResolver::class,
diff --git a/dev/tests/unit/Util/MockModuleResolverBuilder.php b/dev/tests/unit/Util/MockModuleResolverBuilder.php
new file mode 100644
index 000000000..0e1b6fc31
--- /dev/null
+++ b/dev/tests/unit/Util/MockModuleResolverBuilder.php
@@ -0,0 +1,61 @@
+ '/base/path/some/other/path/Magento/Module'];
+
+ /**
+ * Mock ModuleResolver builder
+ *
+ * @param array $paths
+ * @return void
+ * @throws \Exception
+ */
+ public function setup($paths = null)
+ {
+ if (empty($paths)) {
+ $paths = $this->defaultPaths;
+ }
+
+ $mockConfig = AspectMock::double(MftfApplicationConfig::class, ['forceGenerateEnabled' => false]);
+ $instance = AspectMock::double(ObjectManager::class, ['create' => $mockConfig->make(), 'get' => null])->make();
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+
+ $property = new \ReflectionProperty(ModuleResolver::class, 'instance');
+ $property->setAccessible(true);
+ $property->setValue(null);
+
+ $mockResolver = AspectMock::double(
+ ModuleResolver::class,
+ [
+ 'getAdminToken' => false,
+ 'globRelevantPaths' => [],
+ 'getEnabledModules' => []
+ ]
+ );
+ $instance = AspectMock::double(ObjectManager::class, ['create' => $mockResolver->make(), 'get' => null])
+ ->make();
+ AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]);
+
+ $resolver = ModuleResolver::getInstance();
+ $property = new \ReflectionProperty(ModuleResolver::class, 'enabledModuleNameAndPaths');
+ $property->setAccessible(true);
+ $property->setValue($resolver, $paths);
+ }
+}
diff --git a/dev/tests/unit/Util/TestDataArrayBuilder.php b/dev/tests/unit/Util/TestDataArrayBuilder.php
index aeeeae850..eb192e040 100644
--- a/dev/tests/unit/Util/TestDataArrayBuilder.php
+++ b/dev/tests/unit/Util/TestDataArrayBuilder.php
@@ -225,7 +225,7 @@ public function withFileName($filename = null)
*/
public function withTestReference($reference = null)
{
- if ($reference != null) {
+ if ($reference !== null) {
$this->testReference = $reference;
}
diff --git a/dev/tests/verification/TestModule/Page/SamplePage.xml b/dev/tests/verification/TestModule/Page/SamplePage.xml
index bf8f99615..59efded6e 100644
--- a/dev/tests/verification/TestModule/Page/SamplePage.xml
+++ b/dev/tests/verification/TestModule/Page/SamplePage.xml
@@ -8,25 +8,25 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/etc/config/.env.example b/etc/config/.env.example
index f4aa90e51..f25cb3de7 100644
--- a/etc/config/.env.example
+++ b/etc/config/.env.example
@@ -43,7 +43,7 @@ BROWSER=chrome
#DEFAULT_TIMEZONE=America/Los_Angeles
#*** These properties impact the modules loaded into MFTF, you can point to your own full path, or a custom set of modules located with the core set
-MODULE_WHITELIST=Magento_Framework,Magento_ConfigurableProductWishlist,Magento_ConfigurableProductCatalogSearch
+MODULE_WHITELIST=Magento_Framework,ConfigurableProductWishlist,ConfigurableProductCatalogSearch
#CUSTOM_MODULE_PATHS=
#*** Bool property which allows the user to toggle debug output during test execution
diff --git a/src/Magento/FunctionalTestingFramework/Composer/AbstractComposer.php b/src/Magento/FunctionalTestingFramework/Composer/AbstractComposer.php
new file mode 100644
index 000000000..98f72d0c1
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Composer/AbstractComposer.php
@@ -0,0 +1,86 @@
+[^,\s]+_[^,\s]+)/';
+
+ /**#@+
+ * Composer package array keys
+ */
+ const PACKAGE_NAME = 'name';
+ const PACKAGE_TYPE = 'type';
+ const PACKAGE_VERSION = 'version';
+ const PACKAGE_DESCRIPTION = 'description';
+ const PACKAGE_INSTALLEDPATH = 'installedPath';
+ const PACKAGE_REQUIRES = 'requires';
+ const PACKAGE_DEVREQUIRES = 'devRequires';
+ const PACKAGE_SUGGESTS = 'suggests';
+ const PACKAGE_SUGGESTED_MAGENTO_MODULES = 'suggestedMagentoModules';
+ /**#@-*/
+
+ /**
+ * @var \Composer\Composer
+ */
+ protected $composer;
+
+ /**
+ * @param string $composerFile
+ */
+ public function __construct($composerFile)
+ {
+ $this->composer = \Composer\Factory::create(new BufferIO(), $composerFile);
+ }
+
+ /**
+ * Get composer
+ *
+ * @return \Composer\Composer
+ */
+ protected function getComposer()
+ {
+ return $this->composer;
+ }
+
+ /**
+ * Parse input array and return all suggested magento module names, i.e. an example "suggest" in composer.json
+ *
+ * "suggest": {
+ * "magento/module-backend": "type: magento2-module, name: Magento_Backend, version: ~100.0.0",
+ * "magento/module-store": "type: magento2-module, name: Magento_Store, version: ~100.0.0"
+ * }
+ *
+ * @param array $suggests
+ * @return array
+ */
+ protected function parseSuggestsForMagentoModuleNames($suggests)
+ {
+ $magentoModuleNames = [];
+ foreach ($suggests as $suggest) {
+ // Expecting pattern - type: magento2-module, name: Magento_Store, version: ~100.0.0
+ preg_match(self::MODULE_NAME_IN_SUGGEST_REGEX, $suggest, $match);
+ if (isset($match[self::MODULE_NAME_IN_SUGGEST_REGEX_INDEX])) {
+ $magentoModuleNames[] = $match[self::MODULE_NAME_IN_SUGGEST_REGEX_INDEX];
+ }
+ }
+
+ return array_unique($magentoModuleNames);
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Composer/ComposerInstall.php b/src/Magento/FunctionalTestingFramework/Composer/ComposerInstall.php
new file mode 100644
index 000000000..f10c0b418
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Composer/ComposerInstall.php
@@ -0,0 +1,102 @@
+isInstalledPackageOfType($packageName, self::TEST_MODULE_PACKAGE_TYPE);
+ }
+
+ /**
+ * Determines if package is a magento package
+ *
+ * @param string $packageName
+ * @return boolean
+ */
+ public function isMagentoPackage($packageName)
+ {
+ return $this->isInstalledPackageOfType($packageName, self::MAGENTO_MODULE_PACKAGE_TYPE);
+ }
+
+ /**
+ * Determines if an installed package is of a certain type
+ *
+ * @param string $packageName
+ * @param string $packageType
+ * @return boolean
+ */
+ public function isInstalledPackageOfType($packageName, $packageType)
+ {
+ /** @var CompletePackageInterface $package */
+ foreach ($this->getLocker()->getLockedRepository()->getPackages() as $package) {
+ if (($package->getName() == $packageName) && ($package->getType() == $packageType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Collect all installed mftf test packages from composer lock
+ *
+ * @return array
+ */
+ public function getInstalledTestPackages()
+ {
+ $packages = [];
+ /** @var CompletePackageInterface $package */
+ foreach ($this->getLocker()->getLockedRepository()->getPackages() as $package) {
+ if ($package->getType() == self::TEST_MODULE_PACKAGE_TYPE) {
+ $packages[$package->getName()] = [
+ self::PACKAGE_NAME => $package->getName(),
+ self::PACKAGE_TYPE => $package->getType(),
+ self::PACKAGE_VERSION => $package->getPrettyVersion(),
+ self::PACKAGE_DESCRIPTION => $package->getDescription(),
+ self::PACKAGE_SUGGESTS => $package->getSuggests(),
+ self::PACKAGE_REQUIRES => $package->getRequires(),
+ self::PACKAGE_DEVREQUIRES => $package->getDevRequires(),
+ self::PACKAGE_SUGGESTED_MAGENTO_MODULES => $this->parseSuggestsForMagentoModuleNames(
+ $package->getSuggests()
+ ),
+ self::PACKAGE_INSTALLEDPATH => $this->getComposer()->getInstallationManager()
+ ->getInstallPath($package)
+ ];
+ }
+ }
+ return $packages;
+ }
+
+ /**
+ * Load locker
+ *
+ * @return \Composer\Package\Locker
+ */
+ private function getLocker()
+ {
+ if (!$this->locker) {
+ $this->locker = $this->getComposer()->getLocker();
+ }
+ return $this->locker;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Composer/ComposerPackage.php b/src/Magento/FunctionalTestingFramework/Composer/ComposerPackage.php
new file mode 100644
index 000000000..0a6bfdca2
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Composer/ComposerPackage.php
@@ -0,0 +1,161 @@
+getRootPackage();
+ return $package->getPrettyName();
+ }
+
+ /**
+ * Retrieve package type from composer json
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getType();
+ }
+
+ /**
+ * Retrieve package version from composer json
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getPrettyVersion();
+ }
+
+ /**
+ * Retrieve package description from composer json
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getDescription();
+ }
+
+ /**
+ * Retrieve package require from composer json
+ *
+ * @return array
+ */
+ public function getRequires()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getRequires();
+ }
+
+ /**
+ * Retrieve package dev require from composer json
+ *
+ * @return array
+ */
+ public function getDevRequires()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getDevRequires();
+ }
+
+ /**
+ * Retrieve package suggest from composer json
+ *
+ * @return array
+ */
+ public function getSuggests()
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getRootPackage();
+ return $package->getSuggests();
+ }
+
+ /**
+ * Retrieve magento module names in package's suggest
+ *
+ * @return array
+ */
+ public function getSuggestedMagentoModules()
+ {
+ return $this->parseSuggestsForMagentoModuleNames($this->getSuggests());
+ }
+
+ /**
+ * Determines if package is a mftf test package
+ *
+ * @return boolean
+ */
+ public function isMftfTestPackage()
+ {
+ return ($this->getType() == self::TEST_MODULE_PACKAGE_TYPE) ? true : false;
+ }
+
+ /**
+ * Retrieve packages require for given package name and version
+ *
+ * @param string $name
+ * @param string $version
+ * @return array
+ */
+ public function getRequiresForPackage($name, $version)
+ {
+ /** @var \Composer\Package\CompletePackage $package */
+ $package = $this->getComposer()->getRepositoryManager()->findPackage($name, $version);
+ return $package->getRequires();
+ }
+
+ /**
+ * Check if a package is required in composer json
+ *
+ * @param string $packageName
+ * @return boolean
+ */
+ public function isPackageRequiredInComposerJson($packageName)
+ {
+ return (in_array($packageName, array_keys($this->getRequires()))
+ || in_array($packageName, array_keys($this->getDevRequires()))
+ );
+ }
+
+ /**
+ * Get root package
+ *
+ * @return \Composer\Package\RootPackageInterface
+ */
+ public function getRootPackage()
+ {
+ if (!$this->rootPackage) {
+ $this->rootPackage = $this->getComposer()->getPackage();
+ }
+ return $this->rootPackage;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php
index bb6d08f58..989209461 100644
--- a/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php
+++ b/src/Magento/FunctionalTestingFramework/Page/Config/Dom.php
@@ -87,9 +87,9 @@ public function initDom($xml, $filename = null)
);
$pageNodes = $dom->getElementsByTagName('page');
$currentModule =
- $this->modulePathExtractor->extractModuleName($filename) .
- '_' .
- $this->modulePathExtractor->getExtensionPath($filename);
+ $this->modulePathExtractor->getExtensionPath($filename)
+ . '_'
+ . $this->modulePathExtractor->extractModuleName($filename);
foreach ($pageNodes as $pageNode) {
$pageModule = $pageNode->getAttribute("module");
$pageName = $pageNode->getAttribute("name");
diff --git a/src/Magento/FunctionalTestingFramework/Util/ComposerModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ComposerModuleResolver.php
new file mode 100644
index 000000000..5152989e7
--- /dev/null
+++ b/src/Magento/FunctionalTestingFramework/Util/ComposerModuleResolver.php
@@ -0,0 +1,179 @@
+installedTestModules) {
+ return $this->installedTestModules;
+ }
+
+ if (!file_exists($rootComposerFile) || basename($rootComposerFile, '.json') != 'composer') {
+ throw new TestFrameworkException("Invalid root composer json file: {$rootComposerFile}");
+ }
+
+ $this->installedTestModules = [];
+ $composer = new ComposerInstall($rootComposerFile);
+
+ foreach ($composer->getInstalledTestPackages() as $packageName => $packageData) {
+ $suggestedModuleNames = $packageData[ComposerInstall::PACKAGE_SUGGESTED_MAGENTO_MODULES];
+ $path = $packageData[ComposerInstall::PACKAGE_INSTALLEDPATH];
+ $this->installedTestModules[$path] = $suggestedModuleNames;
+ }
+ return $this->installedTestModules;
+ }
+
+ /**
+ * Get code paths by searching test module composer json file from input directories
+ *
+ * @param array $directories
+ * @return array
+ * @throws TestFrameworkException
+ */
+ public function getTestModulesFromPaths($directories)
+ {
+ if (null !== $this->searchedTestModules) {
+ return $this->searchedTestModules;
+ }
+
+ $this->searchedTestModules = [];
+ foreach ($directories as $directory) {
+ $this->searchedTestModules = array_merge_recursive(
+ $this->searchedTestModules,
+ $this->getTestModules($directory)
+ );
+ }
+ return $this->searchedTestModules;
+ }
+
+ /**
+ * Get code paths by searching test module composer json file from input directory
+ *
+ * @param string $directory
+ * @return array
+ * @throws TestFrameworkException
+ */
+ private function getTestModules($directory)
+ {
+ $normalizedDir = realpath($directory);
+ if (!is_dir($normalizedDir)) {
+ throw new TestFrameworkException("Invalid directory: {$directory}");
+ }
+
+ // Find all composer json files under directory
+ $modules = [];
+ $fileList = $this->findComposerJsonFilesAtDepth($normalizedDir, 2);
+ foreach ($fileList as $file) {
+ // Parse composer json for test module name and path information
+ $composerInfo = new ComposerPackage($file);
+ if ($composerInfo->isMftfTestPackage()) {
+ $modulePath = str_replace(
+ DIRECTORY_SEPARATOR . 'composer.json',
+ '',
+ $file
+ );
+ $suggestedMagentoModuleNames = $composerInfo->getSuggestedMagentoModules();
+ if (array_key_exists($modulePath, $modules)) {
+ $modules[$modulePath] = array_merge($modules[$modulePath], $suggestedMagentoModuleNames);
+ } else {
+ $modules[$modulePath] = $suggestedMagentoModuleNames;
+ }
+ }
+ }
+ return $modules;
+ }
+
+ /**
+ * Find absolute paths of all composer json files in a given directory
+ *
+ * @param string $directory
+ * @return array
+ */
+ private function findAllComposerJsonFiles($directory)
+ {
+ $directory = realpath($directory);
+ $jsonPattern = DIRECTORY_SEPARATOR . "composer.json";
+ $subDirectoryPattern = DIRECTORY_SEPARATOR . "*";
+
+ $jsonFileList = glob($directory . $jsonPattern);
+ if ($jsonFileList !== false && !empty($jsonFileList)) {
+ return $jsonFileList;
+ } else {
+ $jsonFileList = [];
+ foreach (glob($directory . $subDirectoryPattern, GLOB_ONLYDIR) as $dir) {
+ $jsonFileList = array_merge_recursive($jsonFileList, self::findAllComposerJsonFiles($dir));
+ }
+ return $jsonFileList;
+ }
+ }
+
+ /**
+ * Find absolute paths of all composer json files in a given directory at certain depths
+ *
+ * @param string $directory
+ * @param integer $depth
+ * @return array
+ */
+ private function findComposerJsonFilesAtDepth($directory, $depth)
+ {
+ $directory = realpath($directory);
+ $jsonPattern = DIRECTORY_SEPARATOR . "composer.json";
+ $subDirectoryPattern = DIRECTORY_SEPARATOR . "*";
+
+ $jsonFileList = [];
+ if ($depth > 0) {
+ foreach (glob($directory . $subDirectoryPattern, GLOB_ONLYDIR) as $dir) {
+ $jsonFileList = array_merge_recursive(
+ $jsonFileList,
+ self::findComposerJsonFilesAtDepth($dir, $depth-1)
+ );
+ }
+ } elseif ($depth == 0) {
+ $jsonFileList = glob($directory . $jsonPattern);
+ if ($jsonFileList === false) {
+ $jsonFileList = [];
+ }
+ }
+ return $jsonFileList;
+ }
+}
diff --git a/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php b/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php
index 33e559ea8..80b556f9a 100644
--- a/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php
+++ b/src/Magento/FunctionalTestingFramework/Util/ModulePathExtractor.php
@@ -11,41 +11,89 @@
*/
class ModulePathExtractor
{
- const MAGENTO = 'Magento';
+ const SPLIT_DELIMITER = '_';
+
+ /**
+ * Test module paths
+ *
+ * @var array
+ */
+ private $testModulePaths = [];
+
+ /**
+ * ModulePathExtractor constructor
+ */
+ public function __construct()
+ {
+ $verbosePath = true;
+ if (empty($this->testModulePaths)) {
+ $this->testModulePaths = ModuleResolver::getInstance()->getModulesPath($verbosePath);
+ }
+ }
/**
* Extracts module name from the path given
+ *
* @param string $path
* @return string
*/
public function extractModuleName($path)
{
- if (empty($path)) {
- return "NO MODULE DETECTED";
- }
- $paths = explode(DIRECTORY_SEPARATOR, $path);
- if (count($paths) < 3) {
+ $key = $this->extractKeyByPath($path);
+ if (empty($key)) {
return "NO MODULE DETECTED";
- } elseif ($paths[count($paths)-3] == "Mftf") {
- // app/code/Magento/[Analytics]/Test/Mftf/Test/SomeText.xml
- return $paths[count($paths)-5];
}
- // dev/tests/acceptance/tests/functional/Magento/FunctionalTest/[Analytics]/Test/SomeText.xml
- return $paths[count($paths)-3];
+ $parts = $this->splitKeyForParts($key);
+ return isset($parts[1]) ? $parts[1] : "NO MODULE DETECTED";
}
/**
- * Extracts the extension form the path, Magento for dev/tests/acceptance, [name] before module otherwise
+ * Extracts vendor name for module from the path given
+ *
* @param string $path
* @return string
*/
public function getExtensionPath($path)
{
- $paths = explode(DIRECTORY_SEPARATOR, $path);
- if ($paths[count($paths)-3] == "Mftf") {
- // app/code/[Magento]/Analytics/Test/Mftf/Test/SomeText.xml
- return $paths[count($paths)-6];
+ $key = $this->extractKeyByPath($path);
+ if (empty($key)) {
+ return "NO VENDOR DETECTED";
+ }
+ $parts = $this->splitKeyForParts($key);
+ return isset($parts[0]) ? $parts[0] : "NO VENDOR DETECTED";
+ }
+
+ /**
+ * Split key by SPLIT_DELIMITER and return parts array
+ *
+ * @param string $key
+ * @return array
+ */
+ private function splitKeyForParts($key)
+ {
+ $parts = explode(self::SPLIT_DELIMITER, $key);
+ return count($parts) == 2 ? $parts : [];
+ }
+
+ /**
+ * Extract module name key by path
+ *
+ * @param string $path
+ * @return string
+ */
+ private function extractKeyByPath($path)
+ {
+ $shortenedPath = dirname(dirname($path));
+ // Ignore this path if we cannot go to parent directory two levels up
+ if (empty($shortenedPath) || $shortenedPath === '.') {
+ return '';
+ }
+
+ foreach ($this->testModulePaths as $key => $value) {
+ if ($value == $shortenedPath) {
+ return $key;
+ }
}
- return self::MAGENTO;
+ return '';
}
}
diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php
index 16e6037fa..c27496dd5 100644
--- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php
+++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php
@@ -15,6 +15,7 @@
* Class ModuleResolver, resolve module path based on enabled modules of target Magento instance.
*
* @api
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ModuleResolver
{
@@ -38,6 +39,25 @@ class ModuleResolver
*/
const REGISTRAR_CLASS = "\Magento\Framework\Component\ComponentRegistrar";
+ const TEST_MFTF_PATTERN = 'Test' . DIRECTORY_SEPARATOR . 'Mftf';
+ const VENDOR = 'vendor';
+ const APP_CODE = 'app' . DIRECTORY_SEPARATOR . "code";
+ const DEV_TESTS = 'dev'
+ . DIRECTORY_SEPARATOR
+ . 'tests'
+ . DIRECTORY_SEPARATOR
+ . 'acceptance'
+ . DIRECTORY_SEPARATOR
+ . 'tests'
+ . DIRECTORY_SEPARATOR
+ . 'functional';
+ const DEPRECATED_DEV_TESTS = DIRECTORY_SEPARATOR
+ . self:: DEV_TESTS
+ . DIRECTORY_SEPARATOR
+ . "Magento"
+ . DIRECTORY_SEPARATOR
+ . "FunctionalTest";
+
/**
* Enabled modules.
*
@@ -52,6 +72,13 @@ class ModuleResolver
*/
protected $enabledModulePaths = null;
+ /**
+ * Name and path for enabled modules
+ *
+ * @var array|null
+ */
+ protected $enabledModuleNameAndPaths = null;
+
/**
* Configuration instance.
*
@@ -110,6 +137,27 @@ class ModuleResolver
'SampleTests', 'SampleTemplates'
];
+ /**
+ * Registered module list in magento system under test
+ *
+ * @var array
+ */
+ private $registeredModuleList = [];
+
+ /**
+ * Composer json based test module paths
+ *
+ * @var array
+ */
+ private $composerJsonModulePaths = null;
+
+ /**
+ * Composer installed test module paths
+ *
+ * @var array
+ */
+ private $composerInstalledModulePaths = null;
+
/**
* Get ModuleResolver instance.
*
@@ -138,6 +186,7 @@ private function __construct()
* Return an array of enabled modules of target Magento instance.
*
* @return array
+ * @throws TestFrameworkException
*/
public function getEnabledModules()
{
@@ -179,50 +228,84 @@ public function getEnabledModules()
return $this->enabledModules;
}
- /**
- * Return an array of module whitelist that not exist in target Magento instance.
- *
- * @return array
- */
- protected function getModuleWhitelist()
- {
- $moduleWhitelist = getenv(self::MODULE_WHITELIST);
-
- if (empty($moduleWhitelist)) {
- return [];
- }
- return array_map('trim', explode(',', $moduleWhitelist));
- }
-
/**
* Return the modules path based on which modules are enabled in the target Magento instance.
*
+ * @param boolean $verbosePath
* @return array
*/
- public function getModulesPath()
+ public function getModulesPath($verbosePath = false)
{
- if (isset($this->enabledModulePaths)) {
+ if (isset($this->enabledModulePaths) && !$verbosePath) {
return $this->enabledModulePaths;
}
+ if (isset($this->enabledModuleNameAndPaths) && $verbosePath) {
+ return $this->enabledModuleNameAndPaths;
+ }
+
+ // Find test modules paths by searching patterns (Test/Mftf, etc)
$allModulePaths = $this->aggregateTestModulePaths();
+ // Find test modules paths by searching test composer.json files
+ $composerBasedModulePaths = $this->aggregateTestModulePathsFromComposerJson();
+
+ // Find test modules paths by querying composer installed packages
+ $composerBasedModulePaths = array_merge(
+ $composerBasedModulePaths,
+ $this->aggregateTestModulePathsFromComposerInstaller()
+ );
+
+ // Merge test module paths altogether
+ $allModulePaths = $this->mergeModulePaths($allModulePaths, $composerBasedModulePaths);
+
+ // Normalize module names if we get registered module names from Magento system
+ $allModulePaths = $this->normalizeModuleNames($allModulePaths);
+
if (MftfApplicationConfig::getConfig()->forceGenerateEnabled()) {
+ $allModulePaths = $this->flipAndFilterModulePathsArray($allModulePaths);
$this->enabledModulePaths = $this->applyCustomModuleMethods($allModulePaths);
return $this->enabledModulePaths;
}
$enabledModules = array_merge($this->getEnabledModules(), $this->getModuleWhitelist());
- $enabledDirectoryPaths = $this->getEnabledDirectoryPaths($enabledModules, $allModulePaths);
-
+ $enabledDirectoryPaths = $this->flipAndFilterModulePathsArray($allModulePaths, $enabledModules);
$this->enabledModulePaths = $this->applyCustomModuleMethods($enabledDirectoryPaths);
+
return $this->enabledModulePaths;
}
+ /**
+ * Sort files according module sequence.
+ *
+ * @param array $files
+ * @return array
+ */
+ public function sortFilesByModuleSequence(array $files)
+ {
+ return $this->sequenceSorter->sort($files);
+ }
+
+ /**
+ * Return an array of module whitelist that not exist in target Magento instance.
+ *
+ * @return array
+ */
+ protected function getModuleWhitelist()
+ {
+ $moduleWhitelist = getenv(self::MODULE_WHITELIST);
+
+ if (empty($moduleWhitelist)) {
+ return [];
+ }
+ return array_map('trim', explode(',', $moduleWhitelist));
+ }
+
/**
* Retrieves all module directories which might contain pertinent test code.
*
* @return array
+ * @throws TestFrameworkException
*/
private function aggregateTestModulePaths()
{
@@ -235,13 +318,14 @@ private function aggregateTestModulePaths()
$modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP;
$modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR);
- $vendorCodePath = DIRECTORY_SEPARATOR . "vendor";
- $appCodePath = DIRECTORY_SEPARATOR . "app" . DIRECTORY_SEPARATOR . "code";
+ $vendorCodePath = DIRECTORY_SEPARATOR . self::VENDOR;
+ $appCodePath = DIRECTORY_SEPARATOR . self::APP_CODE;
$codePathsToPattern = [
$modulePath => '',
- $magentoBaseCodePath . $vendorCodePath => 'Test' . DIRECTORY_SEPARATOR . 'Mftf',
- $magentoBaseCodePath . $appCodePath => 'Test' . DIRECTORY_SEPARATOR . 'Mftf'
+ $magentoBaseCodePath . $vendorCodePath => self::TEST_MFTF_PATTERN,
+ $magentoBaseCodePath . $appCodePath => self::TEST_MFTF_PATTERN,
+ $magentoBaseCodePath . self::DEPRECATED_DEV_TESTS => ''
];
foreach ($codePathsToPattern as $codePath => $pattern) {
@@ -269,8 +353,6 @@ private function globRelevantPaths($testPath, $pattern)
$relevantPaths = $this->globRelevantWrapper($testPath, $pattern);
}
- $allComponents = $this->getRegisteredModuleList();
-
foreach ($relevantPaths as $codePath) {
// Reduce magento/app/code/Magento/AdminGws/ to magento/app/code/Magento/AdminGws to read symlink
// Symlinks must be resolved otherwise they will not match Magento's filepath to the module
@@ -278,9 +360,8 @@ private function globRelevantPaths($testPath, $pattern)
if (is_link($potentialSymlink)) {
$codePath = realpath($potentialSymlink) . DIRECTORY_SEPARATOR . $pattern;
}
-
- $mainModName = array_search($codePath, $allComponents) ?: basename(str_replace($pattern, '', $codePath));
- $modulePaths[$mainModName][] = $codePath;
+ $mainModName = basename(str_replace($pattern, '', $codePath));
+ $modulePaths[$codePath] = [$mainModName];
if (MftfApplicationConfig::getConfig()->verboseEnabled()) {
LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->debug(
@@ -290,6 +371,21 @@ private function globRelevantPaths($testPath, $pattern)
}
}
+ if (strpos($testPath, self::DEPRECATED_DEV_TESTS) !== false && !empty($modulePaths)) {
+ $deprecatedPath = ltrim(self::DEPRECATED_DEV_TESTS, DIRECTORY_SEPARATOR);
+ $suggestedPath = self::DEV_TESTS . DIRECTORY_SEPARATOR . 'Magento';
+ $message = "DEPRECATION: Found MFTF test modules in the deprecated path: $deprecatedPath."
+ . " Move these test modules to $suggestedPath.";
+
+ if (MftfApplicationConfig::getConfig()->verboseEnabled()) {
+ LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->warning($message);
+ }
+ // Suppress print during unit testing
+ if (MftfApplicationConfig::getConfig()->getPhase() !== MftfApplicationConfig::UNIT_TEST_PHASE) {
+ print ("\n$message\n\n");
+ }
+ }
+
return $modulePaths;
}
@@ -314,46 +410,192 @@ private static function globRelevantWrapper($testPath, $pattern)
}
/**
- * Takes a multidimensional array of module paths and flattens to return a one dimensional array of test paths
+ * Aggregate all code paths with test module composer json files
*
- * @param array $modulePaths
* @return array
*/
- private function flattenAllModulePaths($modulePaths)
+ private function aggregateTestModulePathsFromComposerJson()
{
- $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($modulePaths));
- $resultArray = [];
+ // Define the module paths
+ $magentoBaseCodePath = MAGENTO_BP;
- foreach ($it as $value) {
- $resultArray[] = $value;
+ // Define the module paths from default TESTS_MODULE_PATH
+ $modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP;
+ $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR);
+
+ $searchCodePaths = [
+ $magentoBaseCodePath . DIRECTORY_SEPARATOR . self::DEV_TESTS,
+ ];
+
+ // Add TESTS_MODULE_PATH if it's not included
+ if (array_search($modulePath, $searchCodePaths) === false) {
+ $searchCodePaths[] = $modulePath;
}
- return $resultArray;
+ return $this->getComposerJsonTestModulePaths($searchCodePaths);
}
/**
- * Runs through enabled modules and maps them known module paths by name.
- * @param array $enabledModules
- * @param array $allModulePaths
+ * Retrieve all module code paths that have test module composer json files
+ *
+ * @param array $codePaths
* @return array
*/
- private function getEnabledDirectoryPaths($enabledModules, $allModulePaths)
+ private function getComposerJsonTestModulePaths($codePaths)
{
- $enabledDirectoryPaths = [];
- foreach ($enabledModules as $magentoModuleName) {
- if (!isset($this->knownDirectories[$magentoModuleName]) && !isset($allModulePaths[$magentoModuleName])) {
- continue;
- } elseif (isset($this->knownDirectories[$magentoModuleName])
- && !isset($allModulePaths[$magentoModuleName])) {
- LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->warn(
- "Known directory could not match to an existing path.",
- ['knownDirectory' => $magentoModuleName]
- );
+ if (null !== $this->composerJsonModulePaths) {
+ return $this->composerJsonModulePaths;
+ }
+ try {
+ $this->composerJsonModulePaths = [];
+ $resolver = new ComposerModuleResolver();
+ $this->composerJsonModulePaths = $resolver->getTestModulesFromPaths($codePaths);
+ } catch (TestFrameworkException $e) {
+ }
+
+ return $this->composerJsonModulePaths;
+ }
+
+ /**
+ * Aggregate all code paths with composer installed test modules
+ *
+ * @return array
+ */
+ private function aggregateTestModulePathsFromComposerInstaller()
+ {
+ // Define the module paths
+ $magentoBaseCodePath = MAGENTO_BP;
+ $composerFile = $magentoBaseCodePath . DIRECTORY_SEPARATOR . 'composer.json';
+
+ return $this->getComposerInstalledTestModulePaths($composerFile);
+ }
+
+ /**
+ * Retrieve composer installed test module code paths
+ *
+ * @params string $composerFile
+ * @return array
+ */
+ private function getComposerInstalledTestModulePaths($composerFile)
+ {
+ if (null !== $this->composerInstalledModulePaths) {
+ return $this->composerInstalledModulePaths;
+ }
+ try {
+ $this->composerInstalledModulePaths = [];
+ $resolver = new ComposerModuleResolver();
+ $this->composerInstalledModulePaths = $resolver->getComposerInstalledTestModules($composerFile);
+ } catch (TestFrameworkException $e) {
+ }
+
+ return $this->composerInstalledModulePaths;
+ }
+
+ /**
+ * Flip and filter module code paths
+ *
+ * @param array $objectArray
+ * @param array $filterArray
+ * @return array
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ */
+ private function flipAndFilterModulePathsArray($objectArray, $filterArray = null)
+ {
+ $flippedArray = [];
+ foreach ($objectArray as $path => $modules) {
+ // One path maps to one module
+ if (count($modules) == 1) {
+ if (!is_array($filterArray)
+ || (is_array($filterArray) && in_array($modules[0], $filterArray))
+ || isset($this->knownDirectories[$modules[0]])) {
+ if (strpos($modules[0], '_') === false) {
+ $modules[0] = $this->findVendorNameFromPath($path) . '_' . $modules[0];
+ }
+ $flippedArray[$modules[0]] = $path;
+ }
} else {
- $enabledDirectoryPaths[$magentoModuleName] = $allModulePaths[$magentoModuleName];
+ // One path maps to multiple modules
+ if (!is_array($filterArray)) {
+ $flippedArray[$this->findVendorAndModuleNameFromPath($path)] = $path;
+ } else {
+ $skip = false;
+ foreach ($modules as $module) {
+ if (!in_array($module, $filterArray)) {
+ $skip = true;
+ break;
+ }
+ }
+ if (!$skip) {
+ $flippedArray[$this->findVendorAndModuleNameFromPath($path)] = $path;
+ }
+ }
}
}
- return $enabledDirectoryPaths;
+ return $flippedArray;
+ }
+
+ /**
+ * Merge code paths
+ *
+ * @param array $oneToOneArray
+ * @param array $oneToManyArray
+ * @return array
+ */
+ private function mergeModulePaths($oneToOneArray, $oneToManyArray)
+ {
+ $mergedArray = $oneToOneArray;
+ foreach ($oneToManyArray as $path => $modules) {
+ // Do nothing when array_key_exists
+ if (!array_key_exists($path, $oneToOneArray)) {
+ $mergedArray[$path] = $modules;
+ }
+ }
+ return $mergedArray;
+ }
+
+ /**
+ * Normalize module name if registered module list is available
+ *
+ * @param array $codePaths
+ *
+ * @return array
+ */
+ private function normalizeModuleNames($codePaths)
+ {
+ $allComponents = $this->getRegisteredModuleList();
+ if (empty($allComponents)) {
+ return $codePaths;
+ }
+
+ $normalizedCodePaths = [];
+ foreach ($codePaths as $path => $moduleNames) {
+ $mainModName = array_search($path, $allComponents);
+ if ($mainModName) {
+ $normalizedCodePaths[$path] = [$mainModName];
+ } else {
+ $normalizedCodePaths[$path] = $moduleNames;
+ }
+ }
+
+ return $normalizedCodePaths;
+ }
+
+ /**
+ * Takes a multidimensional array of module paths and flattens to return a one dimensional array of test paths
+ *
+ * @param array $modulePaths
+ * @return array
+ */
+ private function flattenAllModulePaths($modulePaths)
+ {
+ $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($modulePaths));
+ $resultArray = [];
+
+ foreach ($it as $value) {
+ $resultArray[] = $value;
+ }
+
+ return $resultArray;
}
/**
@@ -447,17 +689,6 @@ protected function getAdminToken()
return json_decode($response);
}
- /**
- * Sort files according module sequence.
- *
- * @param array $files
- * @return array
- */
- public function sortFilesByModuleSequence(array $files)
- {
- return $this->sequenceSorter->sort($files);
- }
-
/**
* A wrapping method for any custom logic which needs to be applied to the module list
*
@@ -469,13 +700,16 @@ protected function applyCustomModuleMethods($modulesPath)
$modulePathsResult = $this->removeBlacklistModules($modulesPath);
$customModulePaths = $this->getCustomModulePaths();
- array_map(function ($value) {
+ array_map(function ($key, $value) {
LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
"including custom module",
- ['module' => $value]
+ [$key => $value]
);
- }, $customModulePaths);
+ }, array_keys($customModulePaths), $customModulePaths);
+ if (!isset($this->enabledModuleNameAndPaths)) {
+ $this->enabledModuleNameAndPaths = array_merge($modulePathsResult, $customModulePaths);
+ }
return $this->flattenAllModulePaths(array_merge($modulePathsResult, $customModulePaths));
}
@@ -489,6 +723,7 @@ private function removeBlacklistModules($modulePaths)
{
$modulePathsResult = $modulePaths;
foreach ($modulePathsResult as $moduleName => $modulePath) {
+ // Remove module if it is in blacklist
if (in_array($moduleName, $this->getModuleBlacklist())) {
unset($modulePathsResult[$moduleName]);
LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
@@ -508,13 +743,18 @@ private function removeBlacklistModules($modulePaths)
*/
private function getCustomModulePaths()
{
- $customModulePaths = getenv(self::CUSTOM_MODULE_PATHS);
+ $customModulePaths = [];
+ $paths = getenv(self::CUSTOM_MODULE_PATHS);
- if (!$customModulePaths) {
- return [];
+ if (!$paths) {
+ return $customModulePaths;
+ }
+
+ foreach (explode(',', $paths) as $path) {
+ $customModulePaths = [$this->findVendorAndModuleNameFromPath(trim($path)) => $path];
}
- return array_map('trim', explode(',', $customModulePaths));
+ return $customModulePaths;
}
/**
@@ -534,6 +774,10 @@ private function getModuleBlacklist()
*/
private function getRegisteredModuleList()
{
+ if (!empty($this->registeredModuleList)) {
+ return $this->registeredModuleList;
+ }
+
if (array_key_exists('MAGENTO_BP', $_ENV)) {
$autoloadPath = realpath(MAGENTO_BP . "/app/autoload.php");
if ($autoloadPath) {
@@ -556,7 +800,7 @@ private function getRegisteredModuleList()
array_walk($allComponents, function (&$value) {
// Magento stores component paths with unix DIRECTORY_SEPARATOR, need to stay uniform and convert
$value = realpath($value);
- $value .= '/Test/Mftf';
+ $value .= DIRECTORY_SEPARATOR . self::TEST_MFTF_PATTERN;
});
return $allComponents;
} catch (TestFrameworkException $e) {
@@ -575,4 +819,43 @@ private function getBackendUrl()
{
return getenv('MAGENTO_BACKEND_BASE_URL') ?: getenv('MAGENTO_BASE_URL');
}
+
+ /**
+ * Find vendor and module name from path
+ *
+ * @param string $path
+ * @return string
+ */
+ private function findVendorAndModuleNameFromPath($path)
+ {
+ $path = str_replace(DIRECTORY_SEPARATOR . self::TEST_MFTF_PATTERN, '', $path);
+ return $this->findVendorNameFromPath($path) . '_' . basename($path);
+ }
+
+ /**
+ * Find vendor name from path
+ *
+ * @param string $path
+ * @return string
+ */
+ private function findVendorNameFromPath($path)
+ {
+ $possibleVendorName = 'UnknownVendor';
+ $dirPaths = [
+ self::VENDOR,
+ self::APP_CODE,
+ self::DEV_TESTS
+ ];
+
+ foreach ($dirPaths as $dirPath) {
+ $regex = "~.+\\/" . $dirPath . "\/(?<" . self::VENDOR . ">[^\/]+)\/.+~";
+ $match = [];
+ preg_match($regex, $path, $match);
+ if (isset($match[self::VENDOR])) {
+ $possibleVendorName = ucfirst($match[self::VENDOR]);
+ return $possibleVendorName;
+ }
+ }
+ return $possibleVendorName;
+ }
}