diff --git a/.gitignore b/.gitignore
index da2119a16..d39929c6f 100755
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,6 @@ coverage/
.vscode
codeception.yml
dev/tests/functional/MFTF.suite.yml
-dev/tests/functional/_output
\ No newline at end of file
+dev/tests/functional/_output
+dev/mftf.log
+dev/tests/mftf.log
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d403f911..768cad7d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,59 @@
Magento Functional Testing Framework Changelog
================================================
+2.3.0
+-----
+### Enhancements
+* Traceability
+ * MFTF now outputs generation run-time information, warnings, and errors to an `mftf.log` file.
+ * Overall error messages for various generation errors have been improved. Usage of the `--debug` flag provides file-specific errors for all XML-related errors.
+ * Allure Reports now require a unique `story` and `title` combination, to prevent collisions in Allure Report generation.
+ * The `features` annotation now ignores user input and defaults to the module the test lives under (for clear Allure organization).
+ * The `` annotation has been replaced with a `` annotation, allowing for nested `IssueId` elements.
+ * Tests now require the following annotations: `stories`, `title`, `description`, `severity`.
+ * This will be enforced in a future major release.
+* Modularity
+ * MFTF has been decoupled from MagentoCE:
+ * MFTF can now generate and run tests by itself via `bin/mftf` commands.
+ * It is now a top level MagentoCE dependency, and no longer relies on supporting files in MagentoCE.
+ * It can be used as an isolated dependency for Magento projects such as extensions.
+ * `generate:tests` now warns the user if any declared `` has an inconsistent `module` (`Backend` vs `Magento_Backend`)
+ * The `--force` flag now completely ignores checking of the Magento Installation, allowing generation of tests without a Magento Instance to be running.
+* Customizability
+ * Various test materials can now be extended via an `extends="ExistingMaterial"` attribute. This allows for creation of simple copies of any `entity`, `actionGroup`, or `test`, with small modifications.
+ * `test` and `actionGroup` deltas can now be provided in bulk via a `before/after` attribute on the `test` or `actionGroup` element. Deltas provided this way do not need individual `before/after` attributes, and are inserted sequentially.
+ * Secure and sensitive test data can now be stored and used via a new `.credentials` file, with declaration and usage syntax similar to `.env` file references.
+ * A new `` action has been added to allow users to create and use dates according to the given `date` and `format`.
+ * See DevDocs for more information on all above `Customizability` features.
+* Maintainability
+ * New `bin/mftf` commands have been introduced with parity to existing `robo` commands.
+ * `robo` commands are still supported, but will be deprecated in a future major release.
+ * The `mftf upgrade:tests` command has been introduced, which runs all test upgrade scripts against the provided path.
+ * A new upgrade script was created to replace all test material schema paths to instead use a URN path.
+ * The `mftf generate:urn-catalog` command has been introduced to create a URN catalog in PHPStorm to support the above upgrade.
+ * A warning is now shown on generation if a page's url is referenced without specifying the url (`{{page}}` vs `{{page.url}}`).
+ * An error is now thrown if any test materials contain any overriding element (eg different ``s in a `` with the same `name`)
+ * This previously would cause the last read element to override the previous, causing a silent but potentially incorrect test addition.
+ * Test distribution algorithm for `--config parallel` has been enhanced to take average step length into account.
+
+### Fixes
+* `_after` hook of tests now executes if a non test-related failure causes the test to error.
+* Fixed periods in Allure Report showing up as `•`.
+* Fixed Windows incompatibility of relative paths in various files.
+* Suites will no longer generate if they do not contain any tests.
+* Fixed an issue in generation where users could not use javascript variables in `executeJS` actions.
+* Fixed an issue in generation where entity replacement in action-groups replaced all entities with the first reference found.
+* Fixed an issue in generation where `createData` actions inside `actionGroups` could not properly reference the given `createDataKey`.
+* Fixed an issue where `suites` could not generate if they included an `actionGroup` with two arguments.
+* Fixed an issue in generation where calling the same entity twice (with different parameters) would replace both calls with the first resolved value.
+* The `magentoCLI` action now correctly executes the given command if the `MAGENTO_BASE_URL` contains `index.php` after the domain (ex `https://magento.instance/index.php`)
+* The `stepKey` attribute can no longer be an empty.
+* Variable substitution has been enabled for `regex` and `command` attributes in test actions.
+
+### GitHub Issues/Pull requests:
+* [#161](https://github.com/magento/magento2-functional-testing-framework/pull/161) -- MAGETWO-46837: Implementing extension to wait for readiness metrics.
+* [#72](https://github.com/magento/magento2-functional-testing-framework/issues/72) -- declare(strict_types=1) causes static code check failure (fixed in [#154](https://github.com/magento/magento2-functional-testing-framework/pull/154))
+
2.2.0
-----
### Enhancements
diff --git a/RoboFile.php b/RoboFile.php
deleted file mode 100644
index c0af4c72c..000000000
--- a/RoboFile.php
+++ /dev/null
@@ -1,214 +0,0 @@
-_exec('cp -vn .env.example .env');
- $this->_exec('cp -vf codeception.dist.yml codeception.yml');
- $this->_exec('cp -vf dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.dist.yml dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.yml');
- }
-
- /**
- * Duplicate the Example configuration files for the Project.
- * Build the Codeception project.
- *
- * @return void
- */
- function buildProject()
- {
- $this->writeln("This command will be removed in MFTF v3.0.0. Please use bin/mftf build:project instead.\n");
- $this->cloneFiles();
- $this->_exec('vendor'. DIRECTORY_SEPARATOR .'bin'. DIRECTORY_SEPARATOR .'codecept build');
- }
-
- /**
- * Generate all Tests in PHP.
- *
- * @param array $tests
- * @param array $opts
- * @return void
- */
- function generateTests(array $tests, $opts = ['config' => null, 'force' => true, 'nodes' => null, 'debug' => false])
- {
- require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php';
- $GLOBALS['GENERATE_TESTS'] = true;
- if (!$this->isProjectBuilt()) {
- $this->say("Please run bin/mftf build:project and configure your environment (.env) first.");
- exit(\Robo\Result::EXITCODE_ERROR);
- }
- $testsObjects = [];
- foreach ($tests as $test) {
- $testsObjects[] = Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler::getInstance()->getObject($test);
- }
- if ($opts['force']) {
- $GLOBALS['FORCE_PHP_GENERATE'] = true;
- }
- $testsReferencedInSuites = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance()->generateAllSuites($opts['config']);
- \Magento\FunctionalTestingFramework\Util\TestGenerator::getInstance(null, $testsObjects, $opts['debug'])->createAllTestFiles($opts['config'], $opts['nodes'], $testsReferencedInSuites);
- $this->say("Generate Tests Command Run");
- }
-
- /**
- * Check if MFTF has been properly configured
- * @return bool
- */
- private function isProjectBuilt()
- {
- $actorFile = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Magento' . DIRECTORY_SEPARATOR . 'FunctionalTestingFramework' . DIRECTORY_SEPARATOR . '_generated' . DIRECTORY_SEPARATOR . 'AcceptanceTesterActions.php';
-
- $login = !empty(getenv('MAGENTO_ADMIN_USERNAME'));
- $password = !empty(getenv('MAGENTO_ADMIN_PASSWORD'));
- $baseUrl = !empty(getenv('MAGENTO_BASE_URL'));
- $backendName = !empty(getenv('MAGENTO_BACKEND_NAME'));
- $test = (file_exists($actorFile) && $login && $password && $baseUrl && $backendName);
- return $test;
- }
-
- /**
- * Generate a suite based on name(s) passed in as args.
- *
- * @param array $args
- * @throws Exception
- * @return void
- */
- function generateSuite(array $args)
- {
- if (empty($args)) {
- throw new Exception("Please provide suite name(s) after generate:suite command");
- }
-
- require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php';
- $sg = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance();
-
- foreach ($args as $arg) {
- $sg->generateSuite($arg);
- }
- }
-
- /**
- * Run all MFTF tests.
- *
- * @return void
- */
- function mftf()
- {
- $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run MFTF --skip-group skip');
- }
-
- /**
- * Run all Tests with the specified @group tag, excluding @group 'skip'.
- *
- * @param string $args
- * @return void
- */
- function group($args = '')
- {
- $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --verbose --steps --skip-group skip --group')->args($args)->run();
- }
-
- /**
- * Run all Functional tests located under the Directory Path provided.
- *
- * @param string $args
- * @return void
- */
- function folder($args = '')
- {
- $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run ')->args($args)->run();
- }
-
- /**
- * Run all Tests marked with the @group tag 'example'.
- *
- * @return void
- */
- function example()
- {
- $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --group example --skip-group skip');
- }
-
- /**
- * Generate the HTML for the Allure report based on the Test XML output - Allure v1.4.X
- *
- * @return \Robo\Result
- */
- function allure1Generate()
- {
- return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' -o tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .'');
- }
-
- /**
- * Generate the HTML for the Allure report based on the Test XML output - Allure v2.3.X
- *
- * @return \Robo\Result
- */
- function allure2Generate()
- {
- return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' --output tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .' --clean');
- }
-
- /**
- * Open the HTML Allure report - Allure v1.4.X
- *
- * @return void
- */
- function allure1Open()
- {
- $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .'');
- }
-
- /**
- * Open the HTML Allure report - Allure v2.3.X
- *
- * @return void
- */
- function allure2Open()
- {
- $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .'');
- }
-
- /**
- * Generate and open the HTML Allure report - Allure v1.4.X
- *
- * @return void
- */
- function allure1Report()
- {
- $result1 = $this->allure1Generate();
-
- if ($result1->wasSuccessful()) {
- $this->allure1Open();
- }
- }
-
- /**
- * Generate and open the HTML Allure report - Allure v2.3.X
- *
- * @return void
- */
- function allure2Report()
- {
- $result1 = $this->allure2Generate();
-
- if ($result1->wasSuccessful()) {
- $this->allure2Open();
- }
- }
-}
diff --git a/bin/all-checks.bat b/bin/all-checks.bat
new file mode 100644
index 000000000..d910923ce
--- /dev/null
+++ b/bin/all-checks.bat
@@ -0,0 +1,8 @@
+:: Copyright © Magento, Inc. All rights reserved.
+:: See COPYING.txt for license details.
+
+@echo off
+call bin\static-checks.bat
+
+@echo off
+call bin\phpunit-checks.bat
diff --git a/bin/blacklist.txt b/bin/blacklist.txt
index ea81ed858..39b3a6700 100644
--- a/bin/blacklist.txt
+++ b/bin/blacklist.txt
@@ -4,7 +4,6 @@
# THIS FILE CANNOT CONTAIN BLANK LINES #
###################################################################
bin/blacklist.txt
-dev/tests/static/Magento/Sniffs/Annotations/Helper.php
-dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php
-dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php
+dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php
+dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php
dev/tests/verification/_generated
diff --git a/bin/copyright-check.bat b/bin/copyright-check.bat
new file mode 100644
index 000000000..9f2e23bfb
--- /dev/null
+++ b/bin/copyright-check.bat
@@ -0,0 +1,41 @@
+:: Copyright © Magento, Inc. All rights reserved.
+:: See COPYING.txt for license details.
+
+@echo off
+SETLOCAL EnableDelayedExpansion
+SET BLACKLIST_FILE=bin/blacklist.txt
+SET i=0
+
+FOR /F %%x IN ('git ls-tree --full-tree -r --name-only HEAD') DO (
+ SET GOOD_EXT=
+ if "%%~xx"==".php" set GOOD_EXT=1
+ if "%%~xx"==".xml" set GOOD_EXT=1
+ if "%%~xx"==".xsd" set GOOD_EXT=1
+ IF DEFINED GOOD_EXT (
+ SET BLACKLISTED=
+ FOR /F "tokens=* skip=5" %%f IN (%BLACKLIST_FILE%) DO (
+ SET LINE=%%x
+ IF NOT "!LINE!"=="!LINE:%%f=!" (
+ SET BLACKLISTED=1
+ )
+ )
+ IF NOT DEFINED BLACKLISTED (
+ FIND "Copyright © Magento, Inc. All rights reserved." %%x >nul
+ IF ERRORLEVEL 1 (
+ SET /A i+=1
+ SET NO_COPYRIGHT_LIST[!i!]=%%x
+ )
+ )
+ )
+)
+
+IF DEFINED NO_COPYRIGHT_LIST[1] (
+ ECHO THE FOLLOWING FILES ARE MISSING THE MAGENTO COPYRIGHT:
+ ECHO.
+ ECHO Copyright © Magento, Inc. All rights reserved.
+ ECHO See COPYING.txt for license details.
+ ECHO.
+ FOR /L %%a IN (1,1,%i%) DO (
+ ECHO !NO_COPYRIGHT_LIST[%%a]!
+ )
+)
\ No newline at end of file
diff --git a/bin/mftf b/bin/mftf
index 623e3bc61..d70ee75c3 100755
--- a/bin/mftf
+++ b/bin/mftf
@@ -11,13 +11,30 @@ if (PHP_SAPI !== 'cli') {
exit(1);
}
+$autoloadPath = realpath(__DIR__ . '/../../../autoload.php');
+$testBootstrapPath = realpath(__DIR__ . '/../dev/tests/functional/_bootstrap.php');
+
+try {
+ if (file_exists($autoloadPath)) {
+ require_once $autoloadPath;
+ } else {
+ require_once $testBootstrapPath;
+ }
+} catch (\Exception $e) {
+ echo 'Autoload error: ' . $e->getMessage();
+ exit(1);
+}
+
+
try {
- require_once __DIR__ . '/../bootstrap.php';
$application = new Symfony\Component\Console\Application();
$application->setName('Magento Functional Testing Framework CLI');
- $application->setVersion('1.0.0');
- $application->add(new Magento\FunctionalTestingFramework\Console\SetupEnvCommand());
- $application->add(new Magento\FunctionalTestingFramework\Console\BuildProjectCommand());
+ $application->setVersion('2.3.0');
+ /** @var \Magento\FunctionalTestingFramework\Console\CommandListInterface $commandList */
+ $commandList = new \Magento\FunctionalTestingFramework\Console\CommandList;
+ foreach ($commandList->getCommands() as $command) {
+ $application->add($command);
+ }
$application->run();
} catch (\Exception $e) {
while ($e) {
diff --git a/bin/static-checks.bat b/bin/static-checks.bat
index 0b9402094..5e14dd289 100644
--- a/bin/static-checks.bat
+++ b/bin/static-checks.bat
@@ -5,17 +5,15 @@
@echo off
@echo ===============================PHP CODE SNIFFER REPORT===============================
-call vendor\bin\phpcs .\src --standard=.\dev\tests\static\Magento --ignore=src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php
-call vendor\bin\phpcs .\dev\tests\unit --standard=.\dev\tests\static\Magento
-call vendor\bin\phpcs .\dev\tests\verification --standard=.\dev\tests\static\Magento --ignore=dev\tests\verification\_generated
+call vendor\bin\phpcs --standard=.\dev\tests\static\Magento --ignore=src/Magento/FunctionalTestingFramework/Group,src/Magento/FunctionalTestingFramework/AcceptanceTester.php .\src
+call vendor\bin\phpcs --standard=.\dev\tests\static\Magento .\dev\tests\unit
+call vendor\bin\phpcs --standard=.\dev\tests\static\Magento --ignore=dev/tests/verification/_generated .\dev\tests\verification
@echo ===============================COPY PASTE DETECTOR REPORT===============================
call vendor\bin\phpcpd .\src
-@echo "===============================PHP MESS DETECTOR REPORT===============================
-vendor\bin\phpmd .\src text \dev\tests\static\Magento\CodeMessDetector\ruleset.xml --exclude _generated,src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php
+@echo ===============================PHP MESS DETECTOR REPORT===============================
+call vendor\bin\phpmd --exclude _generated,src\Magento\FunctionalTestingFramework\Group,src\Magento\FunctionalTestingFramework\AcceptanceTester.php .\src text \dev\tests\static\Magento\CodeMessDetector\ruleset.xml
@echo ===============================MAGENTO COPYRIGHT REPORT===============================
-echo msgbox "INFO:Copyright check currently not run as part of .bat implementation" > "%temp%\popup.vbs"
-wscript.exe "%temp%\popup.vbs"
-::bin\copyright-check
+call bin\copyright-check.bat
diff --git a/bootstrap.php b/bootstrap.php
deleted file mode 100644
index 7a708e8a8..000000000
--- a/bootstrap.php
+++ /dev/null
@@ -1,8 +0,0 @@
-0.2",
"fzaninotto/faker": "^1.6",
+ "monolog/monolog": "^1.0",
"mustache/mustache": "~2.5",
"symfony/process": "^2.8 || ^3.1 || ^4.0",
"vlucas/phpdotenv": "^2.4"
},
"require-dev": {
- "squizlabs/php_codesniffer": "1.5.3",
- "sebastian/phpcpd": "~3.0",
+ "squizlabs/php_codesniffer": "~3.2",
+ "sebastian/phpcpd": "~3.0 || ~4.0",
"brainmaestro/composer-git-hooks": "^2.3",
- "codeception/aspect-mock": "^2.0",
- "goaop/framework": "2.1.2",
+ "doctrine/cache": "<1.7.0",
+ "codeception/aspect-mock": "^3.0",
+ "goaop/framework": "2.2.0",
"codacy/coverage": "^1.4",
"phpmd/phpmd": "^2.6.0",
"rregeer/phpunit-coverage-check": "^0.1.4",
@@ -33,6 +35,7 @@
"symfony/stopwatch": "~3.4.6"
},
"autoload": {
+ "files": ["src/Magento/FunctionalTestingFramework/_bootstrap.php"],
"psr-4": {
"Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework",
"MFTF\\": "dev/tests/functional/MFTF"
diff --git a/composer.lock b/composer.lock
index de8728da1..2f6dfca45 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d394ebb149854c00f790da70a8f0a3f3",
+ "content-hash": "d44cd018c6bc05634270f2b317727ad7",
"packages": [
{
"name": "allure-framework/allure-codeception",
@@ -170,20 +170,21 @@
},
{
"name": "codeception/codeception",
- "version": "2.3.9",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
- "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e"
+ "reference": "bca3547632556875f1cdd567d6057cc14fe472b8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e",
- "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e",
+ "url": "https://api.github.com/repos/Codeception/Codeception/zipball/bca3547632556875f1cdd567d6057cc14fe472b8",
+ "reference": "bca3547632556875f1cdd567d6057cc14fe472b8",
"shasum": ""
},
"require": {
- "behat/gherkin": "~4.4.0",
+ "behat/gherkin": "^4.4.0",
+ "codeception/phpunit-wrapper": "^6.0.9|^7.0.6",
"codeception/stub": "^1.0",
"ext-json": "*",
"ext-mbstring": "*",
@@ -191,10 +192,6 @@
"guzzlehttp/guzzle": ">=4.1.4 <7.0",
"guzzlehttp/psr7": "~1.0",
"php": ">=5.4.0 <8.0",
- "phpunit/php-code-coverage": ">=2.2.4 <6.0",
- "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0",
- "sebastian/comparator": ">1.1 <3.0",
- "sebastian/diff": ">=1.4 <3.0",
"symfony/browser-kit": ">=2.7 <5.0",
"symfony/console": ">=2.7 <5.0",
"symfony/css-selector": ">=2.7 <5.0",
@@ -260,7 +257,53 @@
"functional testing",
"unit testing"
],
- "time": "2018-02-26T23:29:41+00:00"
+ "time": "2018-03-31T22:30:43+00:00"
+ },
+ {
+ "name": "codeception/phpunit-wrapper",
+ "version": "6.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Codeception/phpunit-wrapper.git",
+ "reference": "7057e599d97b02b4efb009681a43b327dbce138a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7057e599d97b02b4efb009681a43b327dbce138a",
+ "reference": "7057e599d97b02b4efb009681a43b327dbce138a",
+ "shasum": ""
+ },
+ "require": {
+ "phpunit/php-code-coverage": ">=2.2.4 <6.0",
+ "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0",
+ "sebastian/comparator": ">1.1 <3.0",
+ "sebastian/diff": ">=1.4 <4.0"
+ },
+ "replace": {
+ "codeception/phpunit-wrapper": "*"
+ },
+ "require-dev": {
+ "codeception/specify": "*",
+ "vlucas/phpdotenv": "^2.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Codeception\\PHPUnit\\": "src\\"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Davert",
+ "email": "davert.php@resend.cc"
+ }
+ ],
+ "description": "PHPUnit classes used by Codeception",
+ "time": "2018-06-20T20:08:14+00:00"
},
{
"name": "codeception/stub",
@@ -1618,6 +1661,84 @@
],
"time": "2017-05-10T09:20:27+00:00"
},
+ {
+ "name": "monolog/monolog",
+ "version": "1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
+ "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "psr/log": "~1.0"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "graylog2/gelf-php": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.9",
+ "php-amqplib/php-amqplib": "~2.4",
+ "php-console/php-console": "^3.1.3",
+ "phpunit/phpunit": "~4.5",
+ "phpunit/phpunit-mock-objects": "2.3.0",
+ "ruflin/elastica": ">=0.90 <3.0",
+ "sentry/sentry": "^0.13",
+ "swiftmailer/swiftmailer": "^5.3|^6.0"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-mongo": "Allow sending log messages to a MongoDB server",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
+ "sentry/sentry": "Allow sending log messages to a Sentry server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "http://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "time": "2017-06-19T01:22:40+00:00"
+ },
{
"name": "moontoast/math",
"version": "1.1.2",
@@ -2472,16 +2593,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "6.5.7",
+ "version": "6.5.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "6bd77b57707c236833d2b57b968e403df060c9d9"
+ "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6bd77b57707c236833d2b57b968e403df060c9d9",
- "reference": "6bd77b57707c236833d2b57b968e403df060c9d9",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b",
+ "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b",
"shasum": ""
},
"require": {
@@ -2552,7 +2673,7 @@
"testing",
"xunit"
],
- "time": "2018-02-26T07:01:09+00:00"
+ "time": "2018-04-10T11:38:34+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -4365,25 +4486,26 @@
},
{
"name": "codeception/aspect-mock",
- "version": "2.1.1",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/Codeception/AspectMock.git",
- "reference": "bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba"
+ "reference": "793aad0a4e9f238ffc5a107337f57c98aa29d2cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Codeception/AspectMock/zipball/bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba",
- "reference": "bf3c000599c0dc75ecb52e19dee2b8ed294cf7ba",
+ "url": "https://api.github.com/repos/Codeception/AspectMock/zipball/793aad0a4e9f238ffc5a107337f57c98aa29d2cf",
+ "reference": "793aad0a4e9f238ffc5a107337f57c98aa29d2cf",
"shasum": ""
},
"require": {
- "goaop/framework": "^2.0.0",
- "php": ">=5.6.0",
- "symfony/finder": "~2.4|~3.0"
+ "goaop/framework": "^2.2.0",
+ "php": ">=7.0.0",
+ "phpunit/phpunit": "> 6.0.0",
+ "symfony/finder": "~2.4|~3.0|~4.0"
},
"require-dev": {
- "codeception/base": "~2.1",
+ "codeception/base": "^2.4",
"codeception/specify": "~0.3",
"codeception/verify": "~0.2"
},
@@ -4404,7 +4526,77 @@
}
],
"description": "Experimental Mocking Framework powered by Aspects",
- "time": "2017-10-24T10:20:17+00:00"
+ "time": "2018-03-20T08:44:00+00:00"
+ },
+ {
+ "name": "doctrine/cache",
+ "version": "v1.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/cache.git",
+ "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
+ "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~5.5|~7.0"
+ },
+ "conflict": {
+ "doctrine/common": ">2.2,<2.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8|~5.0",
+ "predis/predis": "~1.0",
+ "satooshi/php-coveralls": "~0.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "Caching library offering an object-oriented API for many cache backends",
+ "homepage": "http://www.doctrine-project.org",
+ "keywords": [
+ "cache",
+ "caching"
+ ],
+ "time": "2017-07-22T12:49:21+00:00"
},
{
"name": "gitonomy/gitlib",
@@ -4464,29 +4656,32 @@
},
{
"name": "goaop/framework",
- "version": "2.1.2",
+ "version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/goaop/framework.git",
- "reference": "6e2a0fe13c1943db02a67588cfd27692bddaffa5"
+ "reference": "152abbffffcba72d2d159b892deb40b0829d0f28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/goaop/framework/zipball/6e2a0fe13c1943db02a67588cfd27692bddaffa5",
- "reference": "6e2a0fe13c1943db02a67588cfd27692bddaffa5",
+ "url": "https://api.github.com/repos/goaop/framework/zipball/152abbffffcba72d2d159b892deb40b0829d0f28",
+ "reference": "152abbffffcba72d2d159b892deb40b0829d0f28",
"shasum": ""
},
"require": {
- "doctrine/annotations": "~1.0",
- "goaop/parser-reflection": "~1.2",
+ "doctrine/annotations": "^1.2.3",
+ "doctrine/cache": "^1.5",
+ "goaop/parser-reflection": "~1.4",
"jakubledl/dissect": "~1.0",
"php": ">=5.6.0"
},
"require-dev": {
"adlawson/vfs": "^0.12",
"doctrine/orm": "^2.5",
- "phpunit/phpunit": "^4.8",
- "symfony/console": "^2.7|^3.0"
+ "phpunit/phpunit": "^5.7",
+ "symfony/console": "^2.7|^3.0",
+ "symfony/filesystem": "^3.3",
+ "symfony/process": "^3.3"
},
"suggest": {
"symfony/console": "Enables the usage of the command-line tool."
@@ -4523,7 +4718,7 @@
"library",
"php"
],
- "time": "2017-07-12T11:46:25+00:00"
+ "time": "2018-01-05T23:07:51+00:00"
},
{
"name": "goaop/parser-reflection",
@@ -5075,61 +5270,37 @@
},
{
"name": "squizlabs/php_codesniffer",
- "version": "1.5.3",
+ "version": "3.3.0",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "396178ada8499ec492363587f037125bf7b07fcc"
+ "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/396178ada8499ec492363587f037125bf7b07fcc",
- "reference": "396178ada8499ec492363587f037125bf7b07fcc",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d86873af43b4aa9d1f39a3601cc0cfcf02b25266",
+ "reference": "d86873af43b4aa9d1f39a3601cc0cfcf02b25266",
"shasum": ""
},
"require": {
+ "ext-simplexml": "*",
"ext-tokenizer": "*",
- "php": ">=5.1.2"
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
},
- "suggest": {
- "phpunit/php-timer": "dev-master"
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
- "scripts/phpcs"
+ "bin/phpcs",
+ "bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
- "dev-phpcs-fixer": "2.0.x-dev"
+ "dev-master": "3.x-dev"
}
},
- "autoload": {
- "classmap": [
- "CodeSniffer.php",
- "CodeSniffer/CLI.php",
- "CodeSniffer/Exception.php",
- "CodeSniffer/File.php",
- "CodeSniffer/Report.php",
- "CodeSniffer/Reporting.php",
- "CodeSniffer/Sniff.php",
- "CodeSniffer/Tokens.php",
- "CodeSniffer/Reports/",
- "CodeSniffer/CommentParser/",
- "CodeSniffer/Tokenizers/",
- "CodeSniffer/DocGenerators/",
- "CodeSniffer/Standards/AbstractPatternSniff.php",
- "CodeSniffer/Standards/AbstractScopeSniff.php",
- "CodeSniffer/Standards/AbstractVariableSniff.php",
- "CodeSniffer/Standards/IncorrectPatternException.php",
- "CodeSniffer/Standards/Generic/Sniffs/",
- "CodeSniffer/Standards/MySource/Sniffs/",
- "CodeSniffer/Standards/PEAR/Sniffs/",
- "CodeSniffer/Standards/PSR1/Sniffs/",
- "CodeSniffer/Standards/PSR2/Sniffs/",
- "CodeSniffer/Standards/Squiz/Sniffs/",
- "CodeSniffer/Standards/Zend/Sniffs/"
- ]
- },
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
@@ -5140,13 +5311,13 @@
"role": "lead"
}
],
- "description": "PHP_CodeSniffer tokenises PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "http://www.squizlabs.com/php-codesniffer",
"keywords": [
"phpcs",
"standards"
],
- "time": "2014-05-01T03:07:07+00:00"
+ "time": "2018-06-06T23:58:19+00:00"
},
{
"name": "symfony/config",
diff --git a/dev/tests/.cache/.gitignore b/dev/tests/.cache/.gitignore
new file mode 100644
index 000000000..86d0cb272
--- /dev/null
+++ b/dev/tests/.cache/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php
index 655e33910..4364b7f9a 100644
--- a/dev/tests/_bootstrap.php
+++ b/dev/tests/_bootstrap.php
@@ -6,21 +6,33 @@
error_reporting(~E_USER_NOTICE);
define('PROJECT_ROOT', dirname(dirname(__DIR__)));
-require_once PROJECT_ROOT . '/vendor/autoload.php';
-require_once 'util/MftfTestCase.php';
+
+$vendorAutoloadPath = realpath(PROJECT_ROOT . '/vendor/autoload.php');
+$mftfTestCasePath = realpath(PROJECT_ROOT . '/dev/tests/util/MftfTestCase.php');
+
+require_once $vendorAutoloadPath;
+require_once $mftfTestCasePath;
// Set up AspectMock
$kernel = \AspectMock\Kernel::getInstance();
$kernel->init([
'debug' => true,
- 'includePaths' => [PROJECT_ROOT . '/src']
+ 'includePaths' => [PROJECT_ROOT . DIRECTORY_SEPARATOR . 'src'],
+ 'cacheDir' => PROJECT_ROOT .
+ DIRECTORY_SEPARATOR .
+ 'dev' .
+ DIRECTORY_SEPARATOR .
+ 'tests' .
+ DIRECTORY_SEPARATOR .
+ '.cache'
]);
// set mftf appplication context
\Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::create(
true,
- \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::GENERATION_PHASE,
- true
+ \Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::UNIT_TEST_PHASE,
+ true,
+ false
);
// Load needed framework env params
@@ -62,6 +74,27 @@
require($unitUtilFile);
}
+
+// Mocks suite files location getter return to get files in verification/_suite Directory
+// This mocks the paths of the suite files but still parses the xml files
+$suiteDirectory = TESTS_BP . DIRECTORY_SEPARATOR . "verification" . DIRECTORY_SEPARATOR . "_suite";
+
+$paths = [
+ $suiteDirectory . DIRECTORY_SEPARATOR . 'functionalSuite.xml',
+ $suiteDirectory . DIRECTORY_SEPARATOR . 'functionalSuiteHooks.xml'
+];
+
+// create and return the iterator for these file paths
+$iterator = new Magento\FunctionalTestingFramework\Util\Iterator\File($paths);
+try {
+ AspectMock\Test::double(
+ Magento\FunctionalTestingFramework\Config\FileResolver\Root::class,
+ ['get' => $iterator]
+ )->make();
+} catch (Exception $e) {
+ echo "Suite directory not mocked.";
+}
+
function sortInterfaces($files)
{
$bottom = [];
diff --git a/dev/tests/functional/MFTF/_bootstrap.php b/dev/tests/functional/MFTF/_bootstrap.php
deleted file mode 100755
index 6a524e867..000000000
--- a/dev/tests/functional/MFTF/_bootstrap.php
+++ /dev/null
@@ -1,7 +0,0 @@
-load();
+//Do not continue running this bootstrap if PHPUnit is calling it
+$fullTrace = debug_backtrace();
+$rootFile = array_values(array_slice($fullTrace, -1))[0]['file'];
+if (strpos($rootFile, "phpunit") !== false) {
+ return;
+}
- if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) {
- throw new Exception('You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter');
- }
+defined('PROJECT_ROOT') || define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__))));
+require_once realpath(PROJECT_ROOT . '/vendor/autoload.php');
- foreach ($_ENV as $key => $var) {
- defined($key) || define($key, $var);
- }
-}
+//Load constants from .env file
defined('FW_BP') || define('FW_BP', PROJECT_ROOT);
// add the debug flag here
@@ -29,7 +23,26 @@
xdebug_disable();
}
-$RELATIVE_TESTS_MODULE_PATH = '/MFTF/FunctionalTest';
+$RELATIVE_TESTS_MODULE_PATH = '/tests/functional/tests/MFTF';
-defined('TESTS_BP') || define('TESTS_BP', __DIR__);
-defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', TESTS_BP . $RELATIVE_TESTS_MODULE_PATH);
+defined('MAGENTO_BP') || define('MAGENTO_BP', PROJECT_ROOT);
+defined('TESTS_BP') || define('TESTS_BP', dirname(dirname(__DIR__)));
+defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', realpath(TESTS_BP . $RELATIVE_TESTS_MODULE_PATH));
+
+if (file_exists(TESTS_BP . DIRECTORY_SEPARATOR . '.env')) {
+ $env = new \Dotenv\Loader(TESTS_BP . DIRECTORY_SEPARATOR . '.env');
+ $env->load();
+
+ foreach ($_ENV as $key => $var) {
+ defined($key) || define($key, $var);
+ }
+
+ defined('MAGENTO_CLI_COMMAND_PATH') || define(
+ 'MAGENTO_CLI_COMMAND_PATH',
+ 'dev/tests/acceptance/utils/command.php'
+ );
+ $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PATH', MAGENTO_CLI_COMMAND_PATH);
+
+ defined('MAGENTO_CLI_COMMAND_PARAMETER') || define('MAGENTO_CLI_COMMAND_PARAMETER', 'command');
+ $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PARAMETER', MAGENTO_CLI_COMMAND_PARAMETER);
+}
diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml b/dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml
similarity index 100%
rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml
rename to dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml
diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml b/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml
similarity index 100%
rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml
rename to dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml
diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml b/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml
similarity index 100%
rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml
rename to dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml
diff --git a/dev/tests/static/Magento/Sniffs/Annotations/Helper.php b/dev/tests/static/Magento/Sniffs/Annotations/Helper.php
deleted file mode 100644
index 53a03b574..000000000
--- a/dev/tests/static/Magento/Sniffs/Annotations/Helper.php
+++ /dev/null
@@ -1,576 +0,0 @@
-
- * @author Marc McIntyre
- * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- * @link http://pear.php.net/package/PHP_CodeSniffer
- *
- * @SuppressWarnings(PHPMD)
- */
-class Helper
-{
- const ERROR_PARSING = 'ErrorParsing';
-
- const AMBIGUOUS_TYPE = 'AmbiguousType';
-
- const MISSING = 'Missing';
-
- const WRONG_STYLE = 'WrongStyle';
-
- const WRONG_END = 'WrongEnd';
-
- const FAILED_PARSE = 'FailedParse';
-
- const CONTENT_AFTER_OPEN = 'ContentAfterOpen';
-
- const MISSING_SHORT = 'MissingShort';
-
- const EMPTY_DOC = 'Empty';
-
- const SPACING_BETWEEN = 'SpacingBetween';
-
- const SPACING_BEFORE_SHORT = 'SpacingBeforeShort';
-
- const SPACING_BEFORE_TAGS = 'SpacingBeforeTags';
-
- const SHORT_SINGLE_LINE = 'ShortSingleLine';
-
- const SHORT_NOT_CAPITAL = 'ShortNotCapital';
-
- const SHORT_FULL_STOP = 'ShortFullStop';
-
- const SPACING_AFTER = 'SpacingAfter';
-
- const SEE_ORDER = 'SeeOrder';
-
- const EMPTY_SEE = 'EmptySee';
-
- const SEE_INDENT = 'SeeIndent';
-
- const DUPLICATE_RETURN = 'DuplicateReturn';
-
- const MISSING_PARAM_TAG = 'MissingParamTag';
-
- const SPACING_AFTER_LONG_NAME = 'SpacingAfterLongName';
-
- const SPACING_AFTER_LONG_TYPE = 'SpacingAfterLongType';
-
- const MISSING_PARAM_TYPE = 'MissingParamType';
-
- const MISSING_PARAM_NAME = 'MissingParamName';
-
- const EXTRA_PARAM_COMMENT = 'ExtraParamComment';
-
- const PARAM_NAME_NO_MATCH = 'ParamNameNoMatch';
-
- const PARAM_NAME_NO_CASE_MATCH = 'ParamNameNoCaseMatch';
-
- const INVALID_TYPE_HINT = 'InvalidTypeHint';
-
- const INCORRECT_TYPE_HINT = 'IncorrectTypeHint';
-
- const TYPE_HINT_MISSING = 'TypeHintMissing';
-
- const INCORRECT_PARAM_VAR_NAME = 'IncorrectParamVarName';
-
- const RETURN_ORDER = 'ReturnOrder';
-
- const MISSING_RETURN_TYPE = 'MissingReturnType';
-
- const INVALID_RETURN = 'InvalidReturn';
-
- const INVALID_RETURN_VOID = 'InvalidReturnVoid';
-
- const INVALID_NO_RETURN = 'InvalidNoReturn';
-
- const INVALID_RETURN_NOT_VOID = 'InvalidReturnNotVoid';
-
- const INCORRECT_INHERIT_DOC = 'IncorrectInheritDoc';
-
- const RETURN_INDENT = 'ReturnIndent';
-
- const MISSING_RETURN = 'MissingReturn';
-
- const RETURN_NOT_REQUIRED = 'ReturnNotRequired';
-
- const INVALID_THROWS = 'InvalidThrows';
-
- const THROWS_NOT_CAPITAL = 'ThrowsNotCapital';
-
- const THROWS_ORDER = 'ThrowsOrder';
-
- const EMPTY_THROWS = 'EmptyThrows';
-
- const THROWS_NO_FULL_STOP = 'ThrowsNoFullStop';
-
- const SPACING_AFTER_PARAMS = 'SpacingAfterParams';
-
- const SPACING_BEFORE_PARAMS = 'SpacingBeforeParams';
-
- const SPACING_BEFORE_PARAM_TYPE = 'SpacingBeforeParamType';
-
- const LONG_NOT_CAPITAL = 'LongNotCapital';
-
- const TAG_NOT_ALLOWED = 'TagNotAllowed';
-
- const DUPLICATE_VAR = 'DuplicateVar';
-
- const VAR_ORDER = 'VarOrder';
-
- const MISSING_VAR_TYPE = 'MissingVarType';
-
- const INCORRECT_VAR_TYPE = 'IncorrectVarType';
-
- const VAR_INDENT = 'VarIndent';
-
- const MISSING_VAR = 'MissingVar';
-
- const MISSING_PARAM_COMMENT = 'MissingParamComment';
-
- const PARAM_COMMENT_NOT_CAPITAL = 'ParamCommentNotCapital';
-
- const PARAM_COMMENT_FULL_STOP = 'ParamCommentFullStop';
-
- // tells phpcs to use the default level
- const ERROR = 0;
-
- // default level of warnings is 5
- const WARNING = 6;
-
- const INFO = 2;
-
- // Lowest possible level.
- const OFF = 1;
-
- const LEVEL = 'level';
-
- const MESSAGE = 'message';
-
- /**
- * Map of Error Type to Error Severity
- *
- * @var array
- */
- protected static $reportingLevel = [
- self::ERROR_PARSING => [self::LEVEL => self::ERROR, self::MESSAGE => '%s'],
- self::FAILED_PARSE => [self::LEVEL => self::ERROR, self::MESSAGE => '%s'],
- self::AMBIGUOUS_TYPE => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Ambiguous type "%s" for %s is NOT recommended',
- ],
- self::MISSING => [self::LEVEL => self::ERROR, self::MESSAGE => 'Missing %s doc comment'],
- self::WRONG_STYLE => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'You must use "/**" style comments for a %s comment',
- ],
- self::WRONG_END => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'You must use "*/" to end a function comment; found "%s"',
- ],
- self::EMPTY_DOC => [self::LEVEL => self::WARNING, self::MESSAGE => '%s doc comment is empty'],
- self::CONTENT_AFTER_OPEN => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The open comment tag must be the only content on the line',
- ],
- self::MISSING_SHORT => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Missing short description in %s doc comment',
- ],
- self::SPACING_BETWEEN => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'There must be exactly one blank line between descriptions in %s comment',
- ],
- self::SPACING_BEFORE_SHORT => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Extra newline(s) found before %s comment short description',
- ],
- self::SPACING_BEFORE_TAGS => [
- self::LEVEL => self::INFO,
- self::MESSAGE => 'There must be exactly one blank line before the tags in %s comment',
- ],
- self::SHORT_SINGLE_LINE => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '%s comment short description must be on a single line',
- ],
- self::SHORT_NOT_CAPITAL => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => '%s comment short description must start with a capital letter',
- ],
- self::SHORT_FULL_STOP => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '%s comment short description must end with a full stop',
- ],
- self::SPACING_AFTER => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Additional blank lines found at end of %s comment',
- ],
- self::SEE_ORDER => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The @see tag is in the wrong order; the tag precedes @return',
- ],
- self::EMPTY_SEE => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Content missing for @see tag in %s comment',
- ],
- self::SEE_INDENT => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '@see tag indented incorrectly; expected 1 spaces but found %s',
- ],
- self::DUPLICATE_RETURN => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Only 1 @return tag is allowed in function comment',
- ],
- self::MISSING_PARAM_TAG => [self::LEVEL => self::ERROR, self::MESSAGE => 'Doc comment for "%s" missing'],
- self::SPACING_AFTER_LONG_NAME => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Expected 1 space after the longest variable name',
- ],
- self::SPACING_AFTER_LONG_TYPE => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Expected 1 space after the longest type',
- ],
- self::MISSING_PARAM_TYPE => [self::LEVEL => self::ERROR, self::MESSAGE => 'Missing type at position %s'],
- self::MISSING_PARAM_NAME => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Missing parameter name at position %s',
- ],
- self::EXTRA_PARAM_COMMENT => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Superfluous doc comment at position %s',
- ],
- self::PARAM_NAME_NO_MATCH => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Doc comment for var %s does not match actual variable name %s at position %s',
- ],
- self::PARAM_NAME_NO_CASE_MATCH => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Doc comment for var %s does not match case of actual variable name %s at position %s',
- ],
- self::INVALID_TYPE_HINT => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Unknown type hint "%s" found for %s at position %s',
- ],
- self::INCORRECT_TYPE_HINT => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Expected type hint "%s"; found "%s" for %s at position %s',
- ],
- self::TYPE_HINT_MISSING => [
- self::LEVEL => self::INFO,
- self::MESSAGE => 'Type hint "%s" missing for %s at position %s',
- ],
- self::INCORRECT_PARAM_VAR_NAME => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Expected "%s"; found "%s" for %s at position %s',
- ],
- self::RETURN_ORDER => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The @return tag is in the wrong order; the tag follows @see (if used)',
- ],
- self::MISSING_RETURN_TYPE => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Return type missing for @return tag in function comment',
- ],
- self::INVALID_RETURN => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Function return type "%s" is invalid',
- ],
- self::INVALID_RETURN_VOID => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Function return type is void, but function contains return statement',
- ],
- self::INVALID_NO_RETURN => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Function return type is not void, but function has no return statement',
- ],
- self::INVALID_RETURN_NOT_VOID => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Function return type is not void, but function is returning void here',
- ],
- self::INCORRECT_INHERIT_DOC => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The incorrect inherit doc tag usage. Should be {@inheritdoc}',
- ],
- self::RETURN_INDENT => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '@return tag indented incorrectly; expected 1 space but found %s',
- ],
- self::MISSING_RETURN => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Missing @return tag in function comment',
- ],
- self::RETURN_NOT_REQUIRED => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => '@return tag is not required for constructor and destructor',
- ],
- self::INVALID_THROWS => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Exception type and comment missing for @throws tag in function comment',
- ],
- self::THROWS_NOT_CAPITAL => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => '@throws tag comment must start with a capital letter',
- ],
- self::THROWS_ORDER => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The @throws tag is in the wrong order; the tag follows @return',
- ],
- self::EMPTY_THROWS => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Comment missing for @throws tag in function comment',
- ],
- self::THROWS_NO_FULL_STOP => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '@throws tag comment must end with a full stop',
- ],
- self::SPACING_AFTER_PARAMS => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Last parameter comment requires a blank newline after it',
- ],
- self::SPACING_BEFORE_PARAMS => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Parameters must appear immediately after the comment',
- ],
- self::SPACING_BEFORE_PARAM_TYPE => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Expected 1 space before variable type',
- ],
- self::LONG_NOT_CAPITAL => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => '%s comment long description must start with a capital letter',
- ],
- self::TAG_NOT_ALLOWED => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => '@%s tag is not allowed in variable comment',
- ],
- self::DUPLICATE_VAR => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Only 1 @var tag is allowed in variable comment',
- ],
- self::VAR_ORDER => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'The @var tag must be the first tag in a variable comment',
- ],
- self::MISSING_VAR_TYPE => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Var type missing for @var tag in variable comment',
- ],
- self::INCORRECT_VAR_TYPE => [
- self::LEVEL => self::ERROR,
- self::MESSAGE => 'Expected "%s"; found "%s" for @var tag in variable comment',
- ],
- self::VAR_INDENT => [
- self::LEVEL => self::OFF,
- self::MESSAGE => '@var tag indented incorrectly; expected 1 space but found %s',
- ],
- self::MISSING_VAR => [
- self::LEVEL => self::WARNING,
- self::MESSAGE => 'Missing @var tag in variable comment',
- ],
- self::MISSING_PARAM_COMMENT => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Missing comment for param "%s" at position %s',
- ],
- self::PARAM_COMMENT_NOT_CAPITAL => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Param comment must start with a capital letter',
- ],
- self::PARAM_COMMENT_FULL_STOP => [
- self::LEVEL => self::OFF,
- self::MESSAGE => 'Param comment must end with a full stop',
- ],
- ];
-
- /**
- * List of allowed types
- *
- * @var string[]
- */
- protected static $allowedTypes = [
- 'array',
- 'boolean',
- 'bool',
- 'float',
- 'integer',
- 'int',
- 'object',
- 'string',
- 'resource',
- 'callable',
- 'true',
- 'false',
- ];
-
- /**
- * The current PHP_CodeSniffer_File object we are processing.
- *
- * @var PHP_CodeSniffer_File
- */
- protected $currentFile = null;
-
- /**
- * Constructor for class.
- *
- * @param PHP_CodeSniffer_File $phpcsFile
- */
- public function __construct(PHP_CodeSniffer_File $phpcsFile)
- {
- $this->currentFile = $phpcsFile;
- }
-
- /**
- * Returns the current file object
- *
- * @return PHP_CodeSniffer_File
- */
- public function getCurrentFile()
- {
- return $this->currentFile;
- }
-
- /**
- * Returns the eol character used in the file
- *
- * @return string
- */
- public function getEolChar()
- {
- return $this->currentFile->eolChar;
- }
-
- /**
- * Returns the array of allowed types for magento standard
- *
- * @return string[]
- */
- public function getAllowedTypes()
- {
- return self::$allowedTypes;
- }
-
- /**
- * This method will add the message as an error or warning depending on the configuration
- *
- * @param int $stackPtr The stack position where the error occurred.
- * @param string $code A violation code unique to the sniff message.
- * @param string[] $data Replacements for the error message.
- * @param int $severity The severity level for this error. A value of 0
- * @return void
- */
- public function addMessage($stackPtr, $code, $data = [], $severity = 0)
- {
- // Does the $code key exist in the report level
- if (array_key_exists($code, self::$reportingLevel)) {
- $message = self::$reportingLevel[$code][self::MESSAGE];
- $level = self::$reportingLevel[$code][self::LEVEL];
- if ($level === self::WARNING || $level === self::INFO || $level === self::OFF) {
- $s = $level;
- if ($severity !== 0) {
- $s = $severity;
- }
- $this->currentFile->addWarning($message, $stackPtr, $code, $data, $s);
- } else {
- $this->currentFile->addError($message, $stackPtr, $code, $data, $severity);
- }
- }
- }
-
- /**
- * Returns if we should filter a particular file
- *
- * @return bool
- */
- public function shouldFilter()
- {
- $shouldFilter = false;
- $filename = $this->getCurrentFile()->getFilename();
- if (preg_match('#(?:/|\\\\)dev(?:/|\\\\)tests(?:/|\\\\)#', $filename)) {
- // TODO: Temporarily blacklist anything in dev/tests until a sweep of dev/tests can be made.
- // This block of the if should be removed leaving only the phtml condition when dev/tests is swept.
- // Skip all dev tests files
- $shouldFilter = true;
- } elseif (preg_match('#(?:/|\\\\)Test(?:/|\\\\)Unit(?:/|\\\\)#', $filename)) {
- $shouldFilter = true;
- } elseif (preg_match('/\\.phtml$/', $filename)) {
- // Skip all phtml files
- $shouldFilter = true;
- }
-
- return $shouldFilter;
- }
-
- /**
- * Determine if text is a class name
- *
- * @param string $class
- * @return bool
- */
- protected function isClassName($class)
- {
- $return = false;
- if (preg_match('/^\\\\?[A-Z]\\w+(?:\\\\\\w+)*?$/', $class)) {
- $return = true;
- }
- return $return;
- }
-
- /**
- * Determine if the text has an ambiguous type
- *
- * @param string $text
- * @param array &$matches Type that was detected as ambiguous is in result.
- * @return bool
- */
- public function isAmbiguous($text, &$matches = [])
- {
- return preg_match('/(mixed)/', $text, $matches);
- }
-
- /**
- * Take the type and suggest the correct one.
- *
- * @param string $type
- * @return string
- */
- public function suggestType($type)
- {
- $suggestedName = null;
- // First check to see if this type is a list of types. If so we break it up and check each
- if (preg_match('/^.*?(?:\|.*)+$/', $type)) {
- // Return list of all types in this string.
- $types = explode('|', $type);
- if (is_array($types)) {
- // Loop over all types and call this method on each.
- $suggestions = [];
- foreach ($types as $t) {
- $suggestions[] = $this->suggestType($t);
- }
- // Now that we have suggestions put them back together.
- $suggestedName = implode('|', $suggestions);
- } else {
- $suggestedName = 'Unknown';
- }
- } elseif ($this->isClassName($type)) {
- // If this looks like a class name.
- $suggestedName = $type;
- } else {
- // Only one type First check if that type is a base one.
- $lowerVarType = strtolower($type);
- if (in_array($lowerVarType, self::$allowedTypes)) {
- $suggestedName = $lowerVarType;
- }
- // If no name suggested yet then call the phpcs version of this method.
- if (empty($suggestedName)) {
- $suggestedName = PHP_CodeSniffer::suggestType($type);
- }
- }
- return $suggestedName;
- }
-}
diff --git a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php b/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php
deleted file mode 100644
index 98fc99a0e..000000000
--- a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php
+++ /dev/null
@@ -1,357 +0,0 @@
-
- * A variable doc comment exists.
- * Short description ends with a full stop.
- * There is a blank line after the short description.
- * There is a blank line between the description and the tags.
- * Check the order, indentation and content of each tag.
- *
- *
- * @author Greg Sherwood
- * @author Marc McIntyre
- * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- * @version Release: @package_version@
- * @link http://pear.php.net/package/PHP_CodeSniffer
- *
- * @SuppressWarnings(PHPMD)
- */
-class RequireAnnotatedAttributesSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff
-{
- /**
- * The header comment parser for the current file.
- *
- * @var PHP_CodeSniffer_CommentParser_ClassCommentParser
- */
- protected $commentParser = null;
-
- /**
- * The sniff helper for stuff shared between the annotations sniffs
- *
- * @var Helper
- */
- protected $helper = null;
-
- /**
- * Extract the var comment docblock
- *
- * @param array $tokens
- * @param string $commentToken
- * @param int $stackPtr The position of the current token in the stack passed in $tokens.
- * @return int|false
- */
- protected function extractVarDocBlock($tokens, $commentToken, $stackPtr)
- {
- $commentEnd = $this->helper->getCurrentFile()->findPrevious($commentToken, $stackPtr - 3);
- $break = false;
- if ($commentEnd !== false && $tokens[$commentEnd]['code'] === T_COMMENT) {
- $this->helper->addMessage($stackPtr, Helper::WRONG_STYLE, ['variable']);
- $break = true;
- } elseif ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT) {
- $this->helper->addMessage($stackPtr, Helper::MISSING, ['variable']);
- $break = true;
- } else {
- // Make sure the comment we have found belongs to us.
- $commentFor = $this->helper->getCurrentFile()->findNext(
- [T_VARIABLE, T_CLASS, T_INTERFACE],
- $commentEnd + 1
- );
- if ($commentFor !== $stackPtr) {
- $this->helper->addMessage($stackPtr, Helper::MISSING, ['variable']);
- $break = true;
- }
- }
- return $break ? false : $commentEnd;
- }
-
- /**
- * Checks for short and long descriptions on variable definitions
- *
- * @param PHP_CodeSniffer_CommentParser_CommentElement $comment
- * @param int $commentStart
- * @return void
- */
- protected function checkForDescription($comment, $commentStart)
- {
- $short = $comment->getShortComment();
- $long = '';
- $newlineCount = 0;
- if (trim($short) === '') {
- $this->helper->addMessage($commentStart, Helper::MISSING_SHORT, ['variable']);
- $newlineCount = 1;
- } else {
- // No extra newline before short description.
- $newlineSpan = strspn($short, $this->helper->getEolChar());
- if ($short !== '' && $newlineSpan > 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SPACING_BEFORE_SHORT, ['variable']);
- }
-
- $newlineCount = substr_count($short, $this->helper->getEolChar()) + 1;
-
- // Exactly one blank line between short and long description.
- $long = $comment->getLongComment();
- if (empty($long) === false) {
- $between = $comment->getWhiteSpaceBetween();
- $newlineBetween = substr_count($between, $this->helper->getEolChar());
- if ($newlineBetween !== 2) {
- $this->helper->addMessage(
- $commentStart + $newlineCount + 1,
- Helper::SPACING_BETWEEN,
- ['variable']
- );
- }
-
- $newlineCount += $newlineBetween;
-
- $testLong = trim($long);
- if (preg_match('|\p{Lu}|u', $testLong[0]) === 0) {
- $this->helper->addMessage(
- $commentStart + $newlineCount,
- Helper::LONG_NOT_CAPITAL,
- ['Variable']
- );
- }
- }
-
- // Short description must be single line and end with a full stop.
- $testShort = trim($short);
- $lastChar = $testShort[strlen($testShort) - 1];
- if (substr_count($testShort, $this->helper->getEolChar()) !== 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_SINGLE_LINE, ['Variable']);
- }
-
- if (preg_match('|\p{Lu}|u', $testShort[0]) === 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_NOT_CAPITAL, ['Variable']);
- }
-
- if ($lastChar !== '.') {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_FULL_STOP, ['Variable']);
- }
- }
- // Exactly one blank line before tags.
- $tags = $this->commentParser->getTagOrders();
- if (count($tags) > 1) {
- $newlineSpan = $comment->getNewlineAfter();
- if ($newlineSpan !== 2) {
- if ($long !== '') {
- $newlineCount += substr_count($long, $this->helper->getEolChar()) - $newlineSpan + 1;
- }
-
- $this->helper->addMessage(
- $commentStart + $newlineCount,
- Helper::SPACING_BEFORE_TAGS,
- ['variable']
- );
- $short = rtrim($short, $this->helper->getEolChar() . ' ');
- }
- }
- }
-
- /**
- * Called to process class member vars.
- *
- * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of the current token
- * in the stack passed in $tokens.
- *
- * @return void
- */
- public function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
- {
- $this->helper = new Helper($phpcsFile);
- // if we should skip this type we should do that
- if ($this->helper->shouldFilter()) {
- return;
- }
- $tokens = $phpcsFile->getTokens();
- $commentToken = [T_COMMENT, T_DOC_COMMENT];
-
- // Extract the var comment docblock.
- $commentEnd = $this->extractVarDocBlock($tokens, $commentToken, $stackPtr);
- if ($commentEnd === false) {
- return;
- }
-
- $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1;
- $commentString = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
-
- // Parse the header comment docblock.
- try {
- $this->commentParser = new PHP_CodeSniffer_CommentParser_MemberCommentParser($commentString, $phpcsFile);
- $this->commentParser->parse();
- } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
- $line = $e->getLineWithinComment() + $commentStart;
- $data = [$e->getMessage()];
- $this->helper->addMessage($line, Helper::ERROR_PARSING, $data);
- return;
- }
-
- $comment = $this->commentParser->getComment();
- if (($comment === null) === true) {
- $this->helper->addMessage($commentStart, Helper::EMPTY_DOC, ['Variable']);
- return;
- }
-
- // The first line of the comment should just be the /** code.
- $eolPos = strpos($commentString, $phpcsFile->eolChar);
- $firstLine = substr($commentString, 0, $eolPos);
- if ($firstLine !== '/**') {
- $this->helper->addMessage($commentStart, Helper::CONTENT_AFTER_OPEN);
- }
-
- // Check for a comment description.
- $this->checkForDescription($comment, $commentStart);
-
- // Check for unknown/deprecated tags.
- $unknownTags = $this->commentParser->getUnknown();
- foreach ($unknownTags as $errorTag) {
- // Unknown tags are not parsed, do not process further.
- $data = [$errorTag['tag']];
- $this->helper->addMessage($commentStart + $errorTag['line'], Helper::TAG_NOT_ALLOWED, $data);
- }
-
- // Check each tag.
- $this->processVar($commentStart, $commentEnd);
- $this->processSees($commentStart);
-
- // The last content should be a newline and the content before
- // that should not be blank. If there is more blank space
- // then they have additional blank lines at the end of the comment.
- $words = $this->commentParser->getWords();
- $lastPos = count($words) - 1;
- if (trim(
- $words[$lastPos - 1]
- ) !== '' || strpos(
- $words[$lastPos - 1],
- $this->currentFile->eolChar
- ) === false || trim(
- $words[$lastPos - 2]
- ) === ''
- ) {
- $this->helper->addMessage($commentEnd, Helper::SPACING_AFTER, ['variable']);
- }
- }
-
- /**
- * Process the var tag.
- *
- * @param int $commentStart The position in the stack where the comment started.
- * @param int $commentEnd The position in the stack where the comment ended.
- *
- * @return void
- */
- protected function processVar($commentStart, $commentEnd)
- {
- $var = $this->commentParser->getVar();
-
- if ($var !== null) {
- $errorPos = $commentStart + $var->getLine();
- $index = array_keys($this->commentParser->getTagOrders(), 'var');
-
- if (count($index) > 1) {
- $this->helper->addMessage($errorPos, Helper::DUPLICATE_VAR);
- return;
- }
-
- if ($index[0] !== 1) {
- $this->helper->addMessage($errorPos, Helper::VAR_ORDER);
- }
-
- $content = $var->getContent();
- if (empty($content) === true) {
- $this->helper->addMessage($errorPos, Helper::MISSING_VAR_TYPE);
- return;
- } else {
- $suggestedType = $this->helper->suggestType($content);
- if ($content !== $suggestedType) {
- $data = [$suggestedType, $content];
- $this->helper->addMessage($errorPos, Helper::INCORRECT_VAR_TYPE, $data);
- } elseif ($this->helper->isAmbiguous($content, $matches)) {
- // Warn about ambiguous types ie array or mixed
- $data = [$matches[1], '@var'];
- $this->helper->addMessage($errorPos, Helper::AMBIGUOUS_TYPE, $data);
- }
- }
-
- $spacing = substr_count($var->getWhitespaceBeforeContent(), ' ');
- if ($spacing !== 1) {
- $data = [$spacing];
- $this->helper->addMessage($errorPos, Helper::VAR_INDENT, $data);
- }
- } else {
- $this->helper->addMessage($commentEnd, Helper::MISSING_VAR);
- }
- }
-
- /**
- * Process the see tags.
- *
- * @param int $commentStart The position in the stack where the comment started.
- *
- * @return void
- */
- protected function processSees($commentStart)
- {
- $sees = $this->commentParser->getSees();
- if (empty($sees) === false) {
- foreach ($sees as $see) {
- $errorPos = $commentStart + $see->getLine();
- $content = $see->getContent();
- if (empty($content) === true) {
- $this->helper->addMessage($errorPos, Helper::EMPTY_SEE, ['variable']);
- continue;
- }
-
- $spacing = substr_count($see->getWhitespaceBeforeContent(), ' ');
- if ($spacing !== 1) {
- $data = [$spacing];
- $this->helper->addMessage($errorPos, Helper::SEE_INDENT, $data);
- }
- }
- }
- }
-
- /**
- * Called to process a normal variable.
- *
- * Not required for this sniff.
- *
- * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this token was found.
- * @param int $stackPtr The position where the double quoted
- * string was found.
- *
- * @return void
- */
- protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
- {
- }
-
- /**
- * Called to process variables found in double quoted strings.
- *
- * Not required for this sniff.
- *
- * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this token was found.
- * @param int $stackPtr The position where the double quoted
- * string was found.
- *
- * @return void
- */
- protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
- {
- }
-}
diff --git a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php b/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php
deleted file mode 100644
index 63dc220b6..000000000
--- a/dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php
+++ /dev/null
@@ -1,694 +0,0 @@
-
- * @author Marc McIntyre
- * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- * @link http://pear.php.net/package/PHP_CodeSniffer
- *
- * @SuppressWarnings(PHPMD)
- */
-class RequireAnnotatedMethodsSniff implements PHP_CodeSniffer_Sniff
-{
- /**
- * The name of the method that we are currently processing.
- *
- * @var string
- */
- private $_methodName = '';
-
- /**
- * The position in the stack where the function token was found.
- *
- * @var int
- */
- private $_functionToken = null;
-
- /**
- * The position in the stack where the class token was found.
- *
- * @var int
- */
- private $_classToken = null;
-
- /**
- * The index of the current tag we are processing.
- *
- * @var int
- */
- private $_tagIndex = 0;
-
- /**
- * The function comment parser for the current method.
- *
- * @var PHP_CodeSniffer_CommentParser_FunctionCommentParser
- */
- protected $commentParser = null;
-
- /**
- * The sniff helper for stuff shared between the annotations sniffs
- *
- * @var Helper
- */
- protected $helper = null;
-
- /**
- * Returns an array of tokens this test wants to listen for.
- *
- * @return array
- */
- public function register()
- {
- return [T_FUNCTION];
- }
-
- /**
- * Processes this test, when one of its tokens is encountered.
- *
- * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of the current token
- * in the stack passed in $tokens.
- *
- * @return void
- */
- public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
- {
- $this->helper = new Helper($phpcsFile);
- // if we should skip this type we should do that
- if ($this->helper->shouldFilter()) {
- return;
- }
-
- $tokens = $phpcsFile->getTokens();
-
- $find = [T_COMMENT, T_DOC_COMMENT, T_CLASS, T_FUNCTION, T_OPEN_TAG];
-
- $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1);
-
- if ($commentEnd === false) {
- return;
- }
-
- // If the token that we found was a class or a function, then this
- // function has no doc comment.
- $code = $tokens[$commentEnd]['code'];
-
- if ($code === T_COMMENT) {
- // The function might actually be missing a comment, and this last comment
- // found is just commenting a bit of code on a line. So if it is not the
- // only thing on the line, assume we found nothing.
- $prevContent = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $commentEnd);
- if ($tokens[$commentEnd]['line'] === $tokens[$commentEnd]['line']) {
- $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']);
- } else {
- $this->helper->addMessage($stackPtr, Helper::WRONG_STYLE, ['function']);
- }
- return;
- } elseif ($code !== T_DOC_COMMENT) {
- $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']);
- return;
- } elseif (trim($tokens[$commentEnd]['content']) !== '*/') {
- $this->helper->addMessage($commentEnd, Helper::WRONG_END, [trim($tokens[$commentEnd]['content'])]);
- return;
- }
-
- // If there is any code between the function keyword and the doc block
- // then the doc block is not for us.
- $ignore = PHP_CodeSniffer_Tokens::$scopeModifiers;
- $ignore[] = T_STATIC;
- $ignore[] = T_WHITESPACE;
- $ignore[] = T_ABSTRACT;
- $ignore[] = T_FINAL;
- $prevToken = $phpcsFile->findPrevious($ignore, $stackPtr - 1, null, true);
- if ($prevToken !== $commentEnd) {
- $this->helper->addMessage($stackPtr, Helper::MISSING, ['function']);
- return;
- }
-
- $this->_functionToken = $stackPtr;
-
- $this->_classToken = null;
- foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
- if ($condition === T_CLASS || $condition === T_INTERFACE) {
- $this->_classToken = $condPtr;
- break;
- }
- }
-
- // Find the first doc comment.
- $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT, $commentEnd - 1, null, true) + 1;
- $commentString = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1);
- $this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
-
- try {
- $this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($commentString, $phpcsFile);
- $this->commentParser->parse();
- } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
- $line = $e->getLineWithinComment() + $commentStart;
- $this->helper->addMessage($line, Helper::FAILED_PARSE, [$e->getMessage()]);
- return;
- }
-
- $comment = $this->commentParser->getComment();
- if (($comment === null) === true) {
- $this->helper->addMessage($commentStart, Helper::EMPTY_DOC, ['Function']);
- return;
- }
-
- // The first line of the comment should just be the /** code.
- $eolPos = strpos($commentString, $phpcsFile->eolChar);
- $firstLine = substr($commentString, 0, $eolPos);
- if ($firstLine !== '/**') {
- $this->helper->addMessage($commentStart, Helper::CONTENT_AFTER_OPEN);
- }
-
- // If the comment has an inherit doc note just move on
- if (preg_match('/\{\@inheritdoc\}/', $commentString)) {
- return;
- } elseif (preg_match('/\{?\@?inherit[dD]oc\}?/', $commentString)) {
- $this->helper->addMessage($commentStart, Helper::INCORRECT_INHERIT_DOC);
- return;
- }
-
- $this->processParams($commentStart, $commentEnd);
- $this->processSees($commentStart);
- $this->processReturn($commentStart, $commentEnd);
- $this->processThrows($commentStart);
-
- // Check for a comment description.
- $short = $comment->getShortComment();
- if (trim($short) === '') {
- $this->helper->addMessage($commentStart, Helper::MISSING_SHORT, ['function']);
- return;
- }
-
- // No extra newline before short description.
- $newlineCount = 0;
- $newlineSpan = strspn($short, $phpcsFile->eolChar);
- if ($short !== '' && $newlineSpan > 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SPACING_BEFORE_SHORT, ['function']);
- }
-
- $newlineCount = substr_count($short, $phpcsFile->eolChar) + 1;
-
- // Exactly one blank line between short and long description.
- $long = $comment->getLongComment();
- if (empty($long) === false) {
- $between = $comment->getWhiteSpaceBetween();
- $newlineBetween = substr_count($between, $phpcsFile->eolChar);
- if ($newlineBetween !== 2) {
- $this->helper->addMessage(
- $commentStart + $newlineCount + 1,
- Helper::SPACING_BETWEEN,
- ['function']
- );
- }
- $newlineCount += $newlineBetween;
- $testLong = trim($long);
- if (preg_match('|\p{Lu}|u', $testLong[0]) === 0) {
- $this->helper->addMessage($commentStart + $newlineCount, Helper::LONG_NOT_CAPITAL, ['Function']);
- }
- }
-
- // Exactly one blank line before tags.
- $params = $this->commentParser->getTagOrders();
- if (count($params) > 1) {
- $newlineSpan = $comment->getNewlineAfter();
- if ($newlineSpan !== 2) {
- if ($long !== '') {
- $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1;
- }
-
- $this->helper->addMessage(
- $commentStart + $newlineCount,
- Helper::SPACING_BEFORE_TAGS,
- ['function']
- );
- $short = rtrim($short, $phpcsFile->eolChar . ' ');
- }
- }
-
- // Short description must be single line and end with a full stop.
- $testShort = trim($short);
- $lastChar = $testShort[strlen($testShort) - 1];
- if (substr_count($testShort, $phpcsFile->eolChar) !== 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_SINGLE_LINE, ['Function']);
- }
-
- if (preg_match('|\p{Lu}|u', $testShort[0]) === 0) {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_NOT_CAPITAL, ['Function']);
- }
-
- if ($lastChar !== '.') {
- $this->helper->addMessage($commentStart + 1, Helper::SHORT_FULL_STOP, ['Function']);
- }
-
- // Check for unknown/deprecated tags.
- // For example call: $this->processUnknownTags($commentStart, $commentEnd);
-
- // The last content should be a newline and the content before
- // that should not be blank. If there is more blank space
- // then they have additional blank lines at the end of the comment.
- $words = $this->commentParser->getWords();
- $lastPos = count($words) - 1;
- if (trim(
- $words[$lastPos - 1]
- ) !== '' || strpos(
- $words[$lastPos - 1],
- $this->helper->getCurrentFile()->eolChar
- ) === false || trim(
- $words[$lastPos - 2]
- ) === ''
- ) {
- $this->helper->addMessage($commentEnd, Helper::SPACING_AFTER, ['function']);
- }
- }
-
- /**
- * Process the see tags.
- *
- * @param int $commentStart The position in the stack where the comment started.
- *
- * @return void
- */
- protected function processSees($commentStart)
- {
- $sees = $this->commentParser->getSees();
- if (empty($sees) === false) {
- $tagOrder = $this->commentParser->getTagOrders();
- $index = array_keys($this->commentParser->getTagOrders(), 'see');
- foreach ($sees as $i => $see) {
- $errorPos = $commentStart + $see->getLine();
- $since = array_keys($tagOrder, 'since');
- if (count($since) === 1 && $this->_tagIndex !== 0) {
- $this->_tagIndex++;
- if ($index[$i] !== $this->_tagIndex) {
- $this->helper->addMessage($errorPos, Helper::SEE_ORDER);
- }
- }
-
- $content = $see->getContent();
- if (empty($content) === true) {
- $this->helper->addMessage($errorPos, Helper::EMPTY_SEE, ['function']);
- continue;
- }
- }
- }
- }
-
- /**
- * Process the return comment of this function comment.
- *
- * @param int $commentStart The position in the stack where the comment started.
- * @param int $commentEnd The position in the stack where the comment ended.
- *
- * @return void
- */
- protected function processReturn($commentStart, $commentEnd)
- {
- // Skip constructor and destructor.
- $className = '';
- if ($this->_classToken !== null) {
- $className = $this->helper->getCurrentFile()->getDeclarationName($this->_classToken);
- $className = strtolower(ltrim($className, '_'));
- }
-
- $methodName = strtolower(ltrim($this->_methodName, '_'));
- $return = $this->commentParser->getReturn();
-
- if ($this->_methodName !== '__construct' && $this->_methodName !== '__destruct') {
- if ($return !== null) {
- $tagOrder = $this->commentParser->getTagOrders();
- $index = array_keys($tagOrder, 'return');
- $errorPos = $commentStart + $return->getLine();
- $content = trim($return->getRawContent());
-
- if (count($index) > 1) {
- $this->helper->addMessage($errorPos, Helper::DUPLICATE_RETURN);
- return;
- }
-
- $since = array_keys($tagOrder, 'since');
- if (count($since) === 1 && $this->_tagIndex !== 0) {
- $this->_tagIndex++;
- if ($index[0] !== $this->_tagIndex) {
- $this->helper->addMessage($errorPos, Helper::RETURN_ORDER);
- }
- }
-
- if (empty($content) === true) {
- $this->helper->addMessage($errorPos, Helper::MISSING_RETURN_TYPE);
- } else {
- // Strip off any comments attached to our content
- $parts = explode(' ', $content);
- $content = $parts[0];
- // Check return type (can be multiple, separated by '|').
- $typeNames = explode('|', $content);
- $suggestedNames = [];
- foreach ($typeNames as $i => $typeName) {
- $suggestedName = $this->helper->suggestType($typeName);
- if (in_array($suggestedName, $suggestedNames) === false) {
- $suggestedNames[] = $suggestedName;
- }
- }
-
- $suggestedType = implode('|', $suggestedNames);
- if ($content !== $suggestedType) {
- $data = [$content];
- $this->helper->addMessage($errorPos, Helper::INVALID_RETURN, $data);
- } elseif ($this->helper->isAmbiguous($typeName, $matches)) {
- // Warn about ambiguous types ie array or mixed
- $data = [$matches[1], '@return'];
- $this->helper->addMessage($errorPos, Helper::AMBIGUOUS_TYPE, $data);
- }
-
- $tokens = $this->helper->getCurrentFile()->getTokens();
-
- // If the return type is void, make sure there is
- // no return statement in the function.
- if ($content === 'void') {
- if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
- $endToken = $tokens[$this->_functionToken]['scope_closer'];
-
- $tokens = $this->helper->getCurrentFile()->getTokens();
- for ($returnToken = $this->_functionToken; $returnToken < $endToken; $returnToken++) {
- if ($tokens[$returnToken]['code'] === T_CLOSURE) {
- $returnToken = $tokens[$returnToken]['scope_closer'];
- continue;
- }
-
- if ($tokens[$returnToken]['code'] === T_RETURN) {
- break;
- }
- }
-
- if ($returnToken !== $endToken) {
- // If the function is not returning anything, just
- // exiting, then there is no problem.
- $semicolon = $this->helper->getCurrentFile()->findNext(
- T_WHITESPACE,
- $returnToken + 1,
- null,
- true
- );
- if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
- $this->helper->addMessage($errorPos, Helper::INVALID_RETURN_VOID);
- }
- }
- }
- } elseif ($content !== 'mixed') {
- // If return type is not void, there needs to be a
- // returns statement somewhere in the function that
- // returns something.
- if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
- $endToken = $tokens[$this->_functionToken]['scope_closer'];
- $returnToken = $this->helper->getCurrentFile()->findNext(
- T_RETURN,
- $this->_functionToken,
- $endToken
- );
- if ($returnToken === false) {
- $this->helper->addMessage($errorPos, Helper::INVALID_NO_RETURN);
- } else {
- $semicolon = $this->helper->getCurrentFile()->findNext(
- T_WHITESPACE,
- $returnToken + 1,
- null,
- true
- );
- if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
- $this->helper->addMessage($returnToken, Helper::INVALID_RETURN_NOT_VOID);
- }
- }
- }
- }
-
- $spacing = substr_count($return->getWhitespaceBeforeValue(), ' ');
- if ($spacing !== 1) {
- $data = [$spacing];
- $this->helper->addMessage($errorPos, Helper::RETURN_INDENT, $data);
- }
- }
- } else {
- $this->helper->addMessage($commentEnd, Helper::MISSING_RETURN);
- }
- } elseif ($return !== null) {
- // No return tag for constructor and destructor.
- $errorPos = $commentStart + $return->getLine();
- $this->helper->addMessage($errorPos, Helper::RETURN_NOT_REQUIRED);
- }
- }
-
- /**
- * Process any throw tags that this function comment has.
- *
- * @param int $commentStart The position in the stack where the comment started.
- *
- * @return void
- */
- protected function processThrows($commentStart)
- {
- if (count($this->commentParser->getThrows()) === 0) {
- return;
- }
-
- $tagOrder = $this->commentParser->getTagOrders();
- $index = array_keys($this->commentParser->getTagOrders(), 'throws');
-
- foreach ($this->commentParser->getThrows() as $i => $throw) {
- $exception = $throw->getValue();
- $content = trim($throw->getComment());
- $errorPos = $commentStart + $throw->getLine();
- if (empty($exception) === true) {
- $this->helper->addMessage($errorPos, Helper::INVALID_THROWS);
- } elseif (empty($content) === true) {
- $this->helper->addMessage($errorPos, Helper::EMPTY_THROWS);
- } else {
- // Assumes that $content is not empty.
- // Starts with a capital letter and ends with a fullstop.
- $firstChar = $content[0];
- if (strtoupper($firstChar) !== $firstChar) {
- $this->helper->addMessage($errorPos, Helper::THROWS_NOT_CAPITAL);
- }
-
- $lastChar = $content[strlen($content) - 1];
- if ($lastChar !== '.') {
- $this->helper->addMessage($errorPos, Helper::THROWS_NO_FULL_STOP);
- }
- }
-
- $since = array_keys($tagOrder, 'since');
- if (count($since) === 1 && $this->_tagIndex !== 0) {
- $this->_tagIndex++;
- if ($index[$i] !== $this->_tagIndex) {
- $this->helper->addMessage($errorPos, Helper::THROWS_ORDER);
- }
- }
- }
- }
-
- /**
- * Process the function parameter comments.
- *
- * @param int $commentStart The position in the stack where
- * the comment started.
- * @param int $commentEnd The position in the stack where
- * the comment ended.
- *
- * @return void
- */
- protected function processParams($commentStart, $commentEnd)
- {
- $realParams = $this->helper->getCurrentFile()->getMethodParameters($this->_functionToken);
- $params = $this->commentParser->getParams();
- $foundParams = [];
-
- if (empty($params) === false) {
- $subStrCount = substr_count(
- $params[count($params) - 1]->getWhitespaceAfter(),
- $this->helper->getCurrentFile()->eolChar
- );
- if ($subStrCount !== 2) {
- $errorPos = $params[count($params) - 1]->getLine() + $commentStart;
- $this->helper->addMessage($errorPos, Helper::SPACING_AFTER_PARAMS);
- }
-
- // Parameters must appear immediately after the comment.
- if ($params[0]->getOrder() !== 2) {
- $errorPos = $params[0]->getLine() + $commentStart;
- $this->helper->addMessage($errorPos, Helper::SPACING_BEFORE_PARAMS);
- }
-
- $previousParam = null;
- $spaceBeforeVar = 10000;
- $spaceBeforeComment = 10000;
- $longestType = 0;
- $longestVar = 0;
-
- foreach ($params as $param) {
- $paramComment = trim($param->getComment());
- $errorPos = $param->getLine() + $commentStart;
-
- // Make sure that there is only one space before the var type.
- if ($param->getWhitespaceBeforeType() !== ' ') {
- $this->helper->addMessage($errorPos, Helper::SPACING_BEFORE_PARAM_TYPE);
- }
-
- $spaceCount = substr_count($param->getWhitespaceBeforeVarName(), ' ');
- if ($spaceCount < $spaceBeforeVar) {
- $spaceBeforeVar = $spaceCount;
- $longestType = $errorPos;
- }
-
- $spaceCount = substr_count($param->getWhitespaceBeforeComment(), ' ');
-
- if ($spaceCount < $spaceBeforeComment && $paramComment !== '') {
- $spaceBeforeComment = $spaceCount;
- $longestVar = $errorPos;
- }
-
- // Make sure they are in the correct order, and have the correct name.
- $pos = $param->getPosition();
- $paramName = $param->getVarName() !== '' ? $param->getVarName() : '[ UNKNOWN ]';
-
- if ($previousParam !== null) {
- $previousName = $previousParam->getVarName() !== '' ? $previousParam->getVarName() : 'UNKNOWN';
- }
-
- // Variable must be one of the supported standard type.
- $typeNames = explode('|', $param->getType());
- foreach ($typeNames as $typeName) {
- $suggestedName = $this->helper->suggestType($typeName);
- if ($typeName !== $suggestedName) {
- $data = [$suggestedName, $typeName, $paramName, $pos];
- $this->helper->addMessage($errorPos, Helper::INCORRECT_PARAM_VAR_NAME, $data);
- } elseif ($this->helper->isAmbiguous($typeName, $matches)) {
- // Warn about ambiguous types ie array or mixed
- $data = [$matches[1], $paramName, ' at position ' . $pos . ' is NOT recommended'];
- $this->helper->addMessage($commentEnd + 2, Helper::AMBIGUOUS_TYPE, $data);
- } elseif (count($typeNames) === 1) {
- // Check type hint for array and custom type.
- $suggestedTypeHint = '';
- if (strpos($suggestedName, 'array') !== false) {
- $suggestedTypeHint = 'array';
- } elseif (strpos($suggestedName, 'callable') !== false) {
- $suggestedTypeHint = 'callable';
- } elseif (in_array($typeName, $this->helper->getAllowedTypes()) === false) {
- $suggestedTypeHint = $suggestedName;
- } else {
- $suggestedTypeHint = $this->helper->suggestType($typeName);
- }
-
- if ($suggestedTypeHint !== '' && isset($realParams[$pos - 1]) === true) {
- $typeHint = $realParams[$pos - 1]['type_hint'];
- if ($typeHint === '') {
- $data = [$suggestedTypeHint, $paramName, $pos];
- $this->helper->addMessage($commentEnd + 2, Helper::TYPE_HINT_MISSING, $data);
- } elseif ($typeHint !== $suggestedTypeHint) {
- $data = [$suggestedTypeHint, $typeHint, $paramName, $pos];
- $this->helper->addMessage($commentEnd + 2, Helper::INCORRECT_TYPE_HINT, $data);
- }
- } elseif ($suggestedTypeHint === '' && isset($realParams[$pos - 1]) === true) {
- $typeHint = $realParams[$pos - 1]['type_hint'];
- if ($typeHint !== '') {
- $data = [$typeHint, $paramName, $pos];
- $this->helper->addMessage($commentEnd + 2, Helper::INVALID_TYPE_HINT, $data);
- }
- }
- }
- }
-
- // Make sure the names of the parameter comment matches the
- // actual parameter.
- if (isset($realParams[$pos - 1]) === true) {
- $realName = $realParams[$pos - 1]['name'];
- $foundParams[] = $realName;
-
- // Append ampersand to name if passing by reference.
- if ($realParams[$pos - 1]['pass_by_reference'] === true) {
- $realName = '&' . $realName;
- }
-
- if ($realName !== $paramName) {
- $code = Helper::PARAM_NAME_NO_MATCH;
- $data = [$paramName, $realName, $pos];
-
- if (strtolower($paramName) === strtolower($realName)) {
- $code = Helper::PARAM_NAME_NO_CASE_MATCH;
- }
-
- $this->helper->addMessage($errorPos, $code, $data);
- }
- } elseif (substr($paramName, -4) !== ',...') {
- // We must have an extra parameter comment.
- $this->helper->addMessage($errorPos, Helper::EXTRA_PARAM_COMMENT, [$pos]);
- }
-
- if ($param->getVarName() === '') {
- $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_NAME, [$pos]);
- }
-
- if ($param->getType() === '') {
- $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_TYPE, [$pos]);
- }
-
- if ($paramComment === '') {
- $data = [$paramName, $pos];
- $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_COMMENT, $data);
- } else {
- // Param comments must start with a capital letter and
- // end with the full stop.
- $firstChar = $paramComment[0];
- if (preg_match('|\p{Lu}|u', $firstChar) === 0) {
- $this->helper->addMessage($errorPos, Helper::PARAM_COMMENT_NOT_CAPITAL);
- }
- $lastChar = $paramComment[strlen($paramComment) - 1];
- if ($lastChar !== '.') {
- $this->helper->addMessage($errorPos, Helper::PARAM_COMMENT_FULL_STOP);
- }
- }
-
- $previousParam = $param;
- }
-
- if ($spaceBeforeVar !== 1 && $spaceBeforeVar !== 10000 && $spaceBeforeComment !== 10000) {
- $this->helper->addMessage($longestType, Helper::SPACING_AFTER_LONG_TYPE);
- }
-
- if ($spaceBeforeComment !== 1 && $spaceBeforeComment !== 10000) {
- $this->helper->addMessage($longestVar, Helper::SPACING_AFTER_LONG_NAME);
- }
- }
-
- $realNames = [];
- foreach ($realParams as $realParam) {
- $realNames[] = $realParam['name'];
- }
-
- // Report missing comments.
- $diff = array_diff($realNames, $foundParams);
- foreach ($diff as $neededParam) {
- if (count($params) !== 0) {
- $errorPos = $params[count($params) - 1]->getLine() + $commentStart;
- } else {
- $errorPos = $commentStart;
- }
-
- $data = [$neededParam];
- $this->helper->addMessage($errorPos, Helper::MISSING_PARAM_TAG, $data);
- }
- }
-}
diff --git a/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php b/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php
index e8e850706..9013f12b6 100644
--- a/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php
+++ b/dev/tests/static/Magento/Sniffs/Arrays/ShortArraySyntaxSniff.php
@@ -5,10 +5,10 @@
*/
namespace Magento\Sniffs\Arrays;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
-class ShortArraySyntaxSniff implements PHP_CodeSniffer_Sniff
+class ShortArraySyntaxSniff implements Sniff
{
/**
* {@inheritdoc}
@@ -21,8 +21,12 @@ public function register()
/**
* {@inheritdoc}
*/
- public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ public function process(File $sourceFile, $stackPtr)
{
- $sourceFile->addError('Short array syntax must be used; expected "[]" but found "array()"', $stackPtr);
+ $sourceFile->addError(
+ 'Short array syntax must be used; expected "[]" but found "array()"',
+ $stackPtr,
+ 'ShortArraySyntax'
+ );
}
}
diff --git a/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php b/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php
new file mode 100644
index 000000000..2bd8be194
--- /dev/null
+++ b/dev/tests/static/Magento/Sniffs/Commenting/FunctionCommentSniff.php
@@ -0,0 +1,663 @@
+
+ * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace Magento\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FunctionCommentSniff as PEARFunctionCommentSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Util\Common;
+
+class FunctionCommentSniff extends PEARFunctionCommentSniff
+{
+
+ /**
+ * The current PHP version.
+ *
+ * @var integer
+ */
+ private $phpVersion = null;
+
+
+ /**
+ * Process the return comment of this function comment.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Skip constructor and destructor.
+ $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
+
+ $return = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@return') {
+ if ($return !== null) {
+ $error = 'Only 1 @return tag is allowed in a function comment';
+ $phpcsFile->addError($error, $tag, 'DuplicateReturn');
+ return;
+ }
+
+ $return = $tag;
+ }
+ }
+
+
+ if ($isSpecialMethod === true) {
+ return;
+ }
+
+ if ($return !== null) {
+ $content = $tokens[($return + 2)]['content'];
+ if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) {
+ $error = 'Return type missing for @return tag in function comment';
+ $phpcsFile->addError($error, $return, 'MissingReturnType');
+ } else {
+ // Support both a return type and a description.
+ $split = preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $content, $returnParts);
+ if (isset($returnParts[1]) === false) {
+ return;
+ }
+
+ $returnType = $returnParts[1];
+
+ // Check return type (can be multiple, separated by '|').
+ $typeNames = explode('|', $returnType);
+ $suggestedNames = array();
+ foreach ($typeNames as $i => $typeName) {
+ $suggestedName = Common::suggestType($typeName);
+ if (in_array($suggestedName, $suggestedNames) === false) {
+ $suggestedNames[] = $suggestedName;
+ }
+ }
+
+ $suggestedType = implode('|', $suggestedNames);
+ if ($returnType !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for function return type';
+ $data = array(
+ $suggestedType,
+ $returnType,
+ );
+ $fix = $phpcsFile->addFixableError($error, $return, 'InvalidReturn', $data);
+ if ($fix === true) {
+ $replacement = $suggestedType;
+ if (empty($returnParts[2]) === false) {
+ $replacement .= $returnParts[2];
+ }
+
+ $phpcsFile->fixer->replaceToken(($return + 2), $replacement);
+ unset($replacement);
+ }
+ }
+
+ // If the return type is void, make sure there is
+ // no return statement in the function.
+ if ($returnType === 'void') {
+ if (isset($tokens[$stackPtr]['scope_closer']) === true) {
+ $endToken = $tokens[$stackPtr]['scope_closer'];
+ for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) {
+ if ($tokens[$returnToken]['code'] === T_CLOSURE
+ || $tokens[$returnToken]['code'] === T_ANON_CLASS
+ ) {
+ $returnToken = $tokens[$returnToken]['scope_closer'];
+ continue;
+ }
+
+ if ($tokens[$returnToken]['code'] === T_RETURN
+ || $tokens[$returnToken]['code'] === T_YIELD
+ || $tokens[$returnToken]['code'] === T_YIELD_FROM
+ ) {
+ break;
+ }
+ }
+
+ if ($returnToken !== $endToken) {
+ // If the function is not returning anything, just
+ // exiting, then there is no problem.
+ $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
+ if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
+ $error = 'Function return type is void, but function contains return statement';
+ $phpcsFile->addError($error, $return, 'InvalidReturnVoid');
+ }
+ }
+ }//end if
+ } else if ($returnType !== 'mixed' && in_array('void', $typeNames, true) === false) {
+ // If return type is not void, there needs to be a return statement
+ // somewhere in the function that returns something.
+ if (isset($tokens[$stackPtr]['scope_closer']) === true) {
+ $endToken = $tokens[$stackPtr]['scope_closer'];
+ $returnToken = $phpcsFile->findNext(array(T_RETURN, T_YIELD, T_YIELD_FROM), $stackPtr, $endToken);
+ if ($returnToken === false) {
+ $error = 'Function return type is not void, but function has no return statement';
+ $phpcsFile->addError($error, $return, 'InvalidNoReturn');
+ } else {
+ $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
+ if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
+ $error = 'Function return type is not void, but function is returning void here';
+ $phpcsFile->addError($error, $returnToken, 'InvalidReturnNotVoid');
+ }
+ }
+ }
+ }//end if
+ }//end if
+ } else {
+ $error = 'Missing @return tag in function comment';
+ $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn');
+ }//end if
+
+ }//end processReturn()
+
+
+ /**
+ * Process any throw tags that this function comment has.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $throws = array();
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($tokens[$tag]['content'] !== '@throws') {
+ continue;
+ }
+
+ $exception = null;
+ $comment = null;
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches);
+ $exception = $matches[1];
+ if (isset($matches[2]) === true && trim($matches[2]) !== '') {
+ $comment = $matches[2];
+ }
+ }
+
+ if ($exception === null) {
+ $error = 'Exception type and comment missing for @throws tag in function comment';
+ $phpcsFile->addError($error, $tag, 'InvalidThrows');
+ } else if ($comment === null) {
+ $error = 'Comment missing for @throws tag in function comment';
+// $phpcsFile->addError($error, $tag, 'EmptyThrows');
+ } else {
+ // Any strings until the next tag belong to this comment.
+ if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
+ $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
+ } else {
+ $end = $tokens[$commentStart]['comment_closer'];
+ }
+
+ for ($i = ($tag + 3); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ $comment .= ' '.$tokens[$i]['content'];
+ }
+ }
+
+ // Starts with a capital letter and ends with a fullstop.
+ $firstChar = $comment{0};
+ if (strtoupper($firstChar) !== $firstChar) {
+ $error = '@throws tag comment must start with a capital letter';
+ $phpcsFile->addError($error, ($tag + 2), 'ThrowsNotCapital');
+ }
+
+ $lastChar = substr($comment, -1);
+ if ($lastChar !== '.') {
+ $error = '@throws tag comment must end with a full stop';
+ $phpcsFile->addError($error, ($tag + 2), 'ThrowsNoFullStop');
+ }
+ }//end if
+ }//end foreach
+
+ }//end processThrows()
+
+
+ /**
+ * Process the function parameter comments.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ * @param int $commentStart The position in the stack where the comment started.
+ *
+ * @return void
+ */
+ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
+ {
+ if ($this->phpVersion === null) {
+ $this->phpVersion = Config::getConfigData('php_version');
+ if ($this->phpVersion === null) {
+ $this->phpVersion = PHP_VERSION_ID;
+ }
+ }
+
+ $tokens = $phpcsFile->getTokens();
+
+ $params = array();
+ $maxType = 0;
+ $maxVar = 0;
+ foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
+ if ($tokens[$tag]['content'] !== '@param') {
+ continue;
+ }
+
+ $type = '';
+ $typeSpace = 0;
+ $var = '';
+ $varSpace = 0;
+ $comment = '';
+ $commentLines = array();
+ if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
+ $matches = array();
+ preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches);
+
+ if (empty($matches) === false) {
+ $typeLen = strlen($matches[1]);
+ $type = trim($matches[1]);
+ $typeSpace = ($typeLen - strlen($type));
+ $typeLen = strlen($type);
+ if ($typeLen > $maxType) {
+ $maxType = $typeLen;
+ }
+ }
+
+ if (isset($matches[2]) === true) {
+ $var = $matches[2];
+ $varLen = strlen($var);
+ if ($varLen > $maxVar) {
+ $maxVar = $varLen;
+ }
+
+ if (isset($matches[4]) === true) {
+ $varSpace = strlen($matches[3]);
+ $comment = $matches[4];
+ $commentLines[] = array(
+ 'comment' => $comment,
+ 'token' => ($tag + 2),
+ 'indent' => $varSpace,
+ );
+
+ // Any strings until the next tag belong to this comment.
+ if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
+ $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
+ } else {
+ $end = $tokens[$commentStart]['comment_closer'];
+ }
+
+ for ($i = ($tag + 3); $i < $end; $i++) {
+ if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
+ $indent = 0;
+ if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
+ $indent = strlen($tokens[($i - 1)]['content']);
+ }
+
+ $comment .= ' '.$tokens[$i]['content'];
+ $commentLines[] = array(
+ 'comment' => $tokens[$i]['content'],
+ 'token' => $i,
+ 'indent' => $indent,
+ );
+ }
+ }
+ } else {
+ $error = 'Missing parameter comment';
+// $phpcsFile->addError($error, $tag, 'MissingParamComment');
+ $commentLines[] = array('comment' => '');
+ }//end if
+ } else {
+ $error = 'Missing parameter name';
+ $phpcsFile->addError($error, $tag, 'MissingParamName');
+ }//end if
+ } else {
+ $error = 'Missing parameter type';
+ $phpcsFile->addError($error, $tag, 'MissingParamType');
+ }//end if
+
+ $params[] = array(
+ 'tag' => $tag,
+ 'type' => $type,
+ 'var' => $var,
+ 'comment' => $comment,
+ 'commentLines' => $commentLines,
+ 'type_space' => $typeSpace,
+ 'var_space' => $varSpace,
+ );
+ }//end foreach
+
+ $realParams = $phpcsFile->getMethodParameters($stackPtr);
+ $foundParams = array();
+
+ // We want to use ... for all variable length arguments, so added
+ // this prefix to the variable name so comparisons are easier.
+ foreach ($realParams as $pos => $param) {
+ if ($param['variable_length'] === true) {
+ $realParams[$pos]['name'] = '...'.$realParams[$pos]['name'];
+ }
+ }
+
+ foreach ($params as $pos => $param) {
+ // If the type is empty, the whole line is empty.
+ if ($param['type'] === '') {
+ continue;
+ }
+
+ // Check the param type value.
+ $typeNames = explode('|', $param['type']);
+ $suggestedTypeNames = array();
+
+ foreach ($typeNames as $typeName) {
+ $suggestedName = Common::suggestType($typeName);
+ $suggestedTypeNames[] = $suggestedName;
+
+ if (count($typeNames) > 1) {
+ continue;
+ }
+
+ // Check type hint for array and custom type.
+ $suggestedTypeHint = '';
+ if (strpos($suggestedName, 'array') !== false || substr($suggestedName, -2) === '[]') {
+ $suggestedTypeHint = 'array';
+ } else if (strpos($suggestedName, 'callable') !== false) {
+ $suggestedTypeHint = 'callable';
+ } else if (strpos($suggestedName, 'callback') !== false) {
+ $suggestedTypeHint = 'callable';
+ } else if (in_array($suggestedName, Common::$allowedTypes) === false) {
+ $suggestedTypeHint = $suggestedName;
+ }
+
+ if ($this->phpVersion >= 70000) {
+ if ($suggestedName === 'string') {
+ $suggestedTypeHint = 'string';
+ } else if ($suggestedName === 'int' || $suggestedName === 'integer') {
+ $suggestedTypeHint = 'int';
+ } else if ($suggestedName === 'float') {
+ $suggestedTypeHint = 'float';
+ } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') {
+ $suggestedTypeHint = 'bool';
+ }
+ }
+
+ if ($suggestedTypeHint !== '' && isset($realParams[$pos]) === true) {
+ $typeHint = $realParams[$pos]['type_hint'];
+ if ($typeHint === '') {
+ $error = 'Type hint "%s" missing for %s';
+ $data = array(
+ $suggestedTypeHint,
+ $param['var'],
+ );
+
+ $errorCode = 'TypeHintMissing';
+ if ($suggestedTypeHint === 'string'
+ || $suggestedTypeHint === 'int'
+ || $suggestedTypeHint === 'float'
+ || $suggestedTypeHint === 'bool'
+ ) {
+ $errorCode = 'Scalar'.$errorCode;
+ }
+
+// $phpcsFile->addError($error, $stackPtr, $errorCode, $data);
+ } else if ($typeHint !== substr($suggestedTypeHint, (strlen($typeHint) * -1))) {
+ $error = 'Expected type hint "%s"; found "%s" for %s';
+ $data = array(
+ $suggestedTypeHint,
+ $typeHint,
+ $param['var'],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'IncorrectTypeHint', $data);
+ }//end if
+ } else if ($suggestedTypeHint === '' && isset($realParams[$pos]) === true) {
+ $typeHint = $realParams[$pos]['type_hint'];
+ if ($typeHint !== '') {
+ $error = 'Unknown type hint "%s" found for %s';
+ $data = array(
+ $typeHint,
+ $param['var'],
+ );
+ $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data);
+ }
+ }//end if
+ }//end foreach
+
+ $suggestedType = implode($suggestedTypeNames, '|');
+ if ($param['type'] !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for parameter type';
+ $data = array(
+ $suggestedType,
+ $param['type'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $suggestedType;
+ $content .= str_repeat(' ', $param['type_space']);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $param['var_space']);
+ if (isset($param['commentLines'][0]) === true) {
+ $content .= $param['commentLines'][0]['comment'];
+ }
+
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = (strlen($param['type']) - strlen($suggestedType));
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ if ($param['var'] === '') {
+ continue;
+ }
+
+ $foundParams[] = $param['var'];
+
+ // Check number of spaces after the type.
+ $this->checkSpacingAfterParamType($phpcsFile, $param, $maxType);
+
+ // Make sure the param name is correct.
+ if (isset($realParams[$pos]) === true) {
+ $realName = $realParams[$pos]['name'];
+ if ($realName !== $param['var']) {
+ $code = 'ParamNameNoMatch';
+ $data = array(
+ $param['var'],
+ $realName,
+ );
+
+ $error = 'Doc comment for parameter %s does not match ';
+ if (strtolower($param['var']) === strtolower($realName)) {
+ $error .= 'case of ';
+ $code = 'ParamNameNoCaseMatch';
+ }
+
+ $error .= 'actual variable name %s';
+
+ $phpcsFile->addError($error, $param['tag'], $code, $data);
+ }
+ } else if (substr($param['var'], -4) !== ',...') {
+ // We must have an extra parameter comment.
+ $error = 'Superfluous parameter comment';
+ $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment');
+ }//end if
+
+ if ($param['comment'] === '') {
+ continue;
+ }
+
+ // Check number of spaces after the var name.
+ $this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar);
+
+ // Param comments must start with a capital letter and end with the full stop.
+ if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) {
+ $error = 'Parameter comment must start with a capital letter';
+ $phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital');
+ }
+
+ $lastChar = substr($param['comment'], -1);
+ if ($lastChar !== '.') {
+ $error = 'Parameter comment must end with a full stop';
+ $phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop');
+ }
+ }//end foreach
+
+ $realNames = array();
+ foreach ($realParams as $realParam) {
+ $realNames[] = $realParam['name'];
+ }
+
+ // Report missing comments.
+ $diff = array_diff($realNames, $foundParams);
+ foreach ($diff as $neededParam) {
+ $error = 'Doc comment for parameter "%s" missing';
+ $data = array($neededParam);
+// $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data);
+ }
+
+ }//end processParams()
+
+
+ /**
+ * Check the spacing after the type of a parameter.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $param The parameter to be checked.
+ * @param int $maxType The maxlength of the longest parameter type.
+ * @param int $spacing The number of spaces to add after the type.
+ *
+ * @return void
+ */
+ protected function checkSpacingAfterParamType(File $phpcsFile, $param, $maxType, $spacing=1)
+ {
+ // Check number of spaces after the type.
+ $spaces = ($maxType - strlen($param['type']) + $spacing);
+ if ($param['type_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter type; %s found';
+ $data = array(
+ $spaces,
+ $param['type_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamType', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $spaces);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $param['var_space']);
+ $content .= $param['commentLines'][0]['comment'];
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = ($param['type_space'] - $spaces);
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ }//end checkSpacingAfterParamType()
+
+
+ /**
+ * Check the spacing after the name of a parameter.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param array $param The parameter to be checked.
+ * @param int $maxVar The maxlength of the longest parameter name.
+ * @param int $spacing The number of spaces to add after the type.
+ *
+ * @return void
+ */
+ protected function checkSpacingAfterParamName(File $phpcsFile, $param, $maxVar, $spacing=1)
+ {
+ // Check number of spaces after the var name.
+ $spaces = ($maxVar - strlen($param['var']) + $spacing);
+ if ($param['var_space'] !== $spaces) {
+ $error = 'Expected %s spaces after parameter name; %s found';
+ $data = array(
+ $spaces,
+ $param['var_space'],
+ );
+
+ $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamName', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+
+ $content = $param['type'];
+ $content .= str_repeat(' ', $param['type_space']);
+ $content .= $param['var'];
+ $content .= str_repeat(' ', $spaces);
+ $content .= $param['commentLines'][0]['comment'];
+ $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
+
+ // Fix up the indent of additional comment lines.
+ foreach ($param['commentLines'] as $lineNum => $line) {
+ if ($lineNum === 0
+ || $param['commentLines'][$lineNum]['indent'] === 0
+ ) {
+ continue;
+ }
+
+ $diff = ($param['var_space'] - $spaces);
+ $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
+ $phpcsFile->fixer->replaceToken(
+ ($param['commentLines'][$lineNum]['token'] - 1),
+ str_repeat(' ', $newIndent)
+ );
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }//end if
+ }//end if
+
+ }//end checkSpacingAfterParamName()
+
+
+}//end class
diff --git a/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php b/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php
new file mode 100644
index 000000000..2a3cb92b2
--- /dev/null
+++ b/dev/tests/static/Magento/Sniffs/Commenting/VariableCommentSniff.php
@@ -0,0 +1,153 @@
+
+ * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace Magento\Sniffs\Commenting;
+
+use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Common;
+
+class VariableCommentSniff extends AbstractVariableSniff
+{
+
+
+ /**
+ * Called to process class member vars.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function processMemberVar(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $ignore = array(
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_VAR,
+ T_STATIC,
+ T_WHITESPACE,
+ );
+
+ $commentEnd = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
+ if ($commentEnd === false
+ || ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT)
+ ) {
+ $phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing');
+ return;
+ }
+
+ if ($tokens[$commentEnd]['code'] === T_COMMENT) {
+ $phpcsFile->addError('You must use "/**" style comments for a member variable comment', $stackPtr, 'WrongStyle');
+ return;
+ }
+
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
+
+ $foundVar = null;
+ foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
+ if ($tokens[$tag]['content'] === '@var') {
+ if ($foundVar !== null) {
+ $error = 'Only one @var tag is allowed in a member variable comment';
+ $phpcsFile->addError($error, $tag, 'DuplicateVar');
+ } else {
+ $foundVar = $tag;
+ }
+ } else if ($tokens[$tag]['content'] === '@see') {
+ // Make sure the tag isn't empty.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
+ $error = 'Content missing for @see tag in member variable comment';
+ $phpcsFile->addError($error, $tag, 'EmptySees');
+ }
+ } else {
+ $error = '%s tag is not allowed in member variable comment';
+ $data = array($tokens[$tag]['content']);
+ $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data);
+ }//end if
+ }//end foreach
+
+ // The @var tag is the only one we require.
+ if ($foundVar === null) {
+ $error = 'Missing @var tag in member variable comment';
+ $phpcsFile->addError($error, $commentEnd, 'MissingVar');
+ return;
+ }
+
+ $firstTag = $tokens[$commentStart]['comment_tags'][0];
+ if ($foundVar !== null && $tokens[$firstTag]['content'] !== '@var') {
+ $error = 'The @var tag must be the first tag in a member variable comment';
+ $phpcsFile->addError($error, $foundVar, 'VarOrder');
+ }
+
+ // Make sure the tag isn't empty and has the correct padding.
+ $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $foundVar, $commentEnd);
+ if ($string === false || $tokens[$string]['line'] !== $tokens[$foundVar]['line']) {
+ $error = 'Content missing for @var tag in member variable comment';
+ $phpcsFile->addError($error, $foundVar, 'EmptyVar');
+ return;
+ }
+
+ $varType = $tokens[($foundVar + 2)]['content'];
+ $suggestedType = Common::suggestType($varType);
+ if ($varType !== $suggestedType) {
+ $error = 'Expected "%s" but found "%s" for @var tag in member variable comment';
+ $data = array(
+ $suggestedType,
+ $varType,
+ );
+
+ $fix = $phpcsFile->addFixableError($error, ($foundVar + 2), 'IncorrectVarType', $data);
+ if ($fix === true) {
+ $phpcsFile->fixer->replaceToken(($foundVar + 2), $suggestedType);
+ }
+ }
+
+ }//end processMemberVar()
+
+
+ /**
+ * Called to process a normal variable.
+ *
+ * Not required for this sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this token was found.
+ * @param int $stackPtr The position where the double quoted
+ * string was found.
+ *
+ * @return void
+ */
+ protected function processVariable(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processVariable()
+
+
+ /**
+ * Called to process variables found in double quoted strings.
+ *
+ * Not required for this sniff.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this token was found.
+ * @param int $stackPtr The position where the double quoted
+ * string was found.
+ *
+ * @return void
+ */
+ protected function processVariableInString(File $phpcsFile, $stackPtr)
+ {
+
+ }//end processVariableInString()
+
+
+}//end class
diff --git a/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php b/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php
index 50020cb29..2abcf0531 100644
--- a/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php
+++ b/dev/tests/static/Magento/Sniffs/Files/LineLengthSniff.php
@@ -5,10 +5,12 @@
*/
namespace Magento\Sniffs\Files;
+use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff as FilesLineLengthSniff;
+
/**
* Line length sniff which ignores long lines in case they contain strings intended for translation.
*/
-class LineLengthSniff extends \Generic_Sniffs_Files_LineLengthSniff
+class LineLengthSniff extends FilesLineLengthSniff
{
/**
* Having previous line content allows to ignore long lines in case of multi-line declaration.
@@ -20,7 +22,7 @@ class LineLengthSniff extends \Generic_Sniffs_Files_LineLengthSniff
/**
* {@inheritdoc}
*/
- protected function checkLineLength(\PHP_CodeSniffer_File $phpcsFile, $stackPtr, $lineContent)
+ protected function checkLineLength($phpcsFile, $stackPtr, $lineContent)
{
$previousLineRegexp = '~__\($|\bPhrase\($~';
$currentLineRegexp = '~__\(.+\)|\bPhrase\(.+\)~';
diff --git a/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php b/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php
index 31796ec4e..b6af3f37c 100644
--- a/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php
+++ b/dev/tests/static/Magento/Sniffs/LiteralNamespaces/LiteralNamespacesSniff.php
@@ -5,13 +5,13 @@
*/
namespace Magento\Sniffs\LiteralNamespaces;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
/**
* Custom phpcs sniff to detect usages of literal class and interface names.
*/
-class LiteralNamespacesSniff implements PHP_CodeSniffer_Sniff
+class LiteralNamespacesSniff implements Sniff
{
/**
* @var string
@@ -37,7 +37,7 @@ public function register()
/**
* @inheritdoc
*/
- public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ public function process(File $sourceFile, $stackPtr)
{
$tokens = $sourceFile->getTokens();
if ($sourceFile->findPrevious(T_STRING_CONCAT, $stackPtr, $stackPtr - 3) ||
@@ -47,8 +47,17 @@ public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
}
$content = trim($tokens[$stackPtr]['content'], "\"'");
+ // replace double slashes from class name for avoiding problems with class autoload
+ if (strpos($content, '\\') !== false) {
+ $content = preg_replace('|\\\{2,}|', '\\', $content);
+ }
+
if (preg_match($this->literalNamespacePattern, $content) === 1 && $this->classExists($content)) {
- $sourceFile->addError("Use ::class notation instead.", $stackPtr);
+ $sourceFile->addError(
+ "Use ::class notation instead.",
+ $stackPtr,
+ 'LiteralClassUsage'
+ );
}
}
diff --git a/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php b/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php
index 095025a3d..928fc3a0d 100644
--- a/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php
+++ b/dev/tests/static/Magento/Sniffs/MicroOptimizations/IsNullSniff.php
@@ -5,10 +5,10 @@
*/
namespace Magento\Sniffs\MicroOptimizations;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
-class IsNullSniff implements PHP_CodeSniffer_Sniff
+class IsNullSniff implements Sniff
{
/**
* @var string
@@ -26,11 +26,15 @@ public function register()
/**
* @inheritdoc
*/
- public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ public function process(File $sourceFile, $stackPtr)
{
$tokens = $sourceFile->getTokens();
if ($tokens[$stackPtr]['content'] === $this->blacklist) {
- $sourceFile->addError("is_null must be avoided. Use strict comparison instead.", $stackPtr);
+ $sourceFile->addError(
+ "is_null must be avoided. Use strict comparison instead.",
+ $stackPtr,
+ 'IsNullUsage'
+ );
}
}
}
diff --git a/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php b/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php
index 2e3e4db2a..1618beb66 100644
--- a/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php
+++ b/dev/tests/static/Magento/Sniffs/NamingConventions/InterfaceNameSniff.php
@@ -5,10 +5,10 @@
*/
namespace Magento\Sniffs\NamingConventions;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
-class InterfaceNameSniff implements PHP_CodeSniffer_Sniff
+class InterfaceNameSniff implements Sniff
{
const INTERFACE_SUFFIX = 'Interface';
@@ -23,7 +23,7 @@ public function register()
/**
* {@inheritdoc}
*/
- public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ public function process(File $sourceFile, $stackPtr)
{
$tokens = $sourceFile->getTokens();
$declarationLine = $tokens[$stackPtr]['line'];
@@ -32,7 +32,11 @@ public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
while ($tokens[$stackPtr]['line'] == $declarationLine) {
if ($tokens[$stackPtr]['type'] == 'T_STRING') {
if (substr($tokens[$stackPtr]['content'], 0 - $suffixLength) != self::INTERFACE_SUFFIX) {
- $sourceFile->addError('Interface should have name that ends with "Interface" suffix.', $stackPtr);
+ $sourceFile->addError(
+ 'Interface should have name that ends with "Interface" suffix.',
+ $stackPtr,
+ 'WrongInterfaceName'
+ );
}
break;
}
diff --git a/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php b/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php
index 3d2d979c3..f41c235a6 100644
--- a/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php
+++ b/dev/tests/static/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php
@@ -5,10 +5,10 @@
*/
namespace Magento\Sniffs\NamingConventions;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
-class ReservedWordsSniff implements PHP_CodeSniffer_Sniff
+class ReservedWordsSniff implements Sniff
{
/**
* The following words cannot be used to name a class, interface or trait,
@@ -45,11 +45,11 @@ public function register()
/**
* Check all namespace parts
*
- * @param PHP_CodeSniffer_File $sourceFile
+ * @param File $sourceFile
* @param int $stackPtr
* @return void
*/
- protected function validateNamespace(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ protected function validateNamespace(File $sourceFile, $stackPtr)
{
$stackPtr += 2;
$tokens = $sourceFile->getTokens();
@@ -74,11 +74,11 @@ protected function validateNamespace(PHP_CodeSniffer_File $sourceFile, $stackPtr
/**
* Check class name not having reserved words
*
- * @param PHP_CodeSniffer_File $sourceFile
+ * @param File $sourceFile
* @param int $stackPtr
* @return void
*/
- protected function validateClass(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ protected function validateClass(File $sourceFile, $stackPtr)
{
$tokens = $sourceFile->getTokens();
$stackPtr += 2; //skip "class" and whitespace
@@ -96,7 +96,7 @@ protected function validateClass(PHP_CodeSniffer_File $sourceFile, $stackPtr)
/**
* {@inheritdoc}
*/
- public function process(PHP_CodeSniffer_File $sourceFile, $stackPtr)
+ public function process(File $sourceFile, $stackPtr)
{
$tokens = $sourceFile->getTokens();
switch ($tokens[$stackPtr]['code']) {
diff --git a/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php b/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php
index 41782b1e2..de3cfc50b 100644
--- a/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php
+++ b/dev/tests/static/Magento/Sniffs/Whitespace/EmptyLineMissedSniff.php
@@ -5,13 +5,13 @@
*/
namespace Magento\Sniffs\Whitespace;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
/**
* Class EmptyLineMissedSniff
*/
-class EmptyLineMissedSniff implements PHP_CodeSniffer_Sniff
+class EmptyLineMissedSniff implements Sniff
{
/**
* {@inheritdoc}
@@ -24,7 +24,7 @@ public function register()
/**
* {@inheritdoc}
*/
- public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
+ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($this->doCheck($phpcsFile, $stackPtr, $tokens)) {
@@ -37,12 +37,12 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
}
/**
- * @param PHP_CodeSniffer_File $phpcsFile
+ * @param File $phpcsFile
* @param int $stackPtr
* @param array $tokens
* @return bool
*/
- private function doCheck(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens)
+ private function doCheck(File $phpcsFile, $stackPtr, $tokens)
{
$result = false;
if ($phpcsFile->hasCondition($stackPtr, T_CLASS) || $phpcsFile->hasCondition($stackPtr, T_INTERFACE)) {
diff --git a/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php b/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php
index 794f316de..f276426ef 100644
--- a/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php
+++ b/dev/tests/static/Magento/Sniffs/Whitespace/MultipleEmptyLinesSniff.php
@@ -5,13 +5,13 @@
*/
namespace Magento\Sniffs\Whitespace;
-use PHP_CodeSniffer_File;
-use PHP_CodeSniffer_Sniff;
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
/**
* Class MultipleEmptyLinesSniff
*/
-class MultipleEmptyLinesSniff implements PHP_CodeSniffer_Sniff
+class MultipleEmptyLinesSniff implements Sniff
{
/**
* {@inheritdoc}
@@ -24,7 +24,7 @@ public function register()
/**
* {@inheritdoc}
*/
- public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
+ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION)
diff --git a/dev/tests/static/Magento/ruleset.xml b/dev/tests/static/Magento/ruleset.xml
index 794b5a8ff..98b4d4241 100644
--- a/dev/tests/static/Magento/ruleset.xml
+++ b/dev/tests/static/Magento/ruleset.xml
@@ -19,6 +19,10 @@
*/_files/*
+
+ */dev/tests*
+
+
@@ -26,4 +30,9 @@
+
+
+
+ 0
+
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php
new file mode 100644
index 000000000..b59858e7f
--- /dev/null
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/Config/Reader/FilesystemTest.php
@@ -0,0 +1,108 @@
+setMockLoggingUtil();
+ }
+
+ /**
+ * Test Reading Empty Files
+ * @throws \Exception
+ */
+ public function testEmptyXmlFile()
+ {
+ // create mocked items and read the file
+ $someFile = $this->setMockFile("somepath.xml", "");
+ $filesystem = $this->createPseudoFileSystem($someFile);
+ $filesystem->read();
+
+ // validate log statement
+ TestLoggingUtil::getInstance()->validateMockLogStatement(
+ "warning",
+ "XML File is empty.",
+ ["File" => "somepath.xml"]
+ );
+ }
+
+ /**
+ * Function used to set mock for File created in test
+ *
+ * @param string $fileName
+ * @param string $content
+ * @return object
+ * @throws \Exception
+ */
+ public function setMockFile($fileName, $content)
+ {
+ $file = AspectMock::double(
+ File::class,
+ [
+ 'current' => "",
+ 'count' => 1,
+ 'getFilename' => $fileName
+ ]
+ )->make();
+
+ //set mocked data property for File
+ $property = new \ReflectionProperty(File::class, 'data');
+ $property->setAccessible(true);
+ $property->setValue($file, [$fileName => $content]);
+
+ return $file;
+ }
+
+ /**
+ * Function used to set mock for filesystem class during test
+ *
+ * @param string $fileList
+ * @return object
+ * @throws \Exception
+ */
+ public function createPseudoFileSystem($fileList)
+ {
+ $filesystem = AspectMock::double(Filesystem::class)->make();
+
+ //set resolver to use mocked resolver
+ $mockFileResolver = AspectMock::double(Module::class, ['get' => $fileList])->make();
+ $property = new \ReflectionProperty(Filesystem::class, 'fileResolver');
+ $property->setAccessible(true);
+ $property->setValue($filesystem, $mockFileResolver);
+
+ //set validator to use mocked validator
+ $mockValidation = AspectMock::double(ValidationState::class, ['isValidationRequired' => false])->make();
+ $property = new \ReflectionProperty(Filesystem::class, 'validationState');
+ $property->setAccessible(true);
+ $property->setValue($filesystem, $mockValidation);
+
+ return $filesystem;
+ }
+
+ /**
+ * After class functionality
+ * @return void
+ */
+ public static function tearDownAfterClass()
+ {
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
+ parent::tearDownAfterClass();
+ }
+}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php
new file mode 100644
index 000000000..a451f8dc9
--- /dev/null
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php
@@ -0,0 +1,38 @@
+ ["$testKey=$testValue"]
+ ]);
+
+ $encryptedCred = CredentialStore::getInstance()->getSecret($testKey);
+
+ // assert the value we've gotten is in fact not identical to our test value
+ $this->assertNotEquals($testValue, $encryptedCred);
+
+ $actualValue = CredentialStore::getInstance()->decryptSecretValue($encryptedCred);
+
+ // assert that we are able to successfully decrypt our secret value
+ $this->assertEquals($testValue, $actualValue);
+ }
+}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
index c09e47478..b77659aa2 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/DataObjectHandlerTest.php
@@ -12,12 +12,12 @@
use Magento\FunctionalTestingFramework\DataGenerator\Parsers\DataProfileSchemaParser;
use Magento\FunctionalTestingFramework\ObjectManager;
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
-use PHPUnit\Framework\TestCase;
+use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
/**
* Class DataObjectHandlerTest
*/
-class DataObjectHandlerTest extends TestCase
+class DataObjectHandlerTest extends MagentoTestCase
{
// All tests share this array, feel free to add but be careful modifying or removing
const PARSER_OUTPUT = [
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
index 3050b3830..b54980314 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/OperationDefinitionObjectHandlerTest.php
@@ -13,12 +13,12 @@
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Parsers\OperationDefinitionParser;
-use PHPUnit\Framework\TestCase;
+use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
/**
* Class OperationDefinitionObjectHandlerTest
*/
-class OperationDefinitionObjectHandlerTest extends TestCase
+class OperationDefinitionObjectHandlerTest extends MagentoTestCase
{
public function testGetMultipleObjects()
{
@@ -62,7 +62,7 @@ public function testGetMultipleObjects()
OperationDefinitionObjectHandler::ENTITY_OPERATION_ENTRY_VALUE => "integer"
],
]
- ]]];
+ ]]];
$this->setMockParserOutput($mockData);
//Perform Assertions
@@ -70,7 +70,6 @@ public function testGetMultipleObjects()
$operations = $operationDefinitionManager->getAllObjects();
$this->assertArrayHasKey($operationType1 . $dataType1, $operations);
$this->assertArrayHasKey($operationType2 . $dataType1, $operations);
-
}
public function testObjectCreation()
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php
index 1b611dba6..5b63f0c94 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php
@@ -6,7 +6,9 @@
namespace Magento\FunctionalTestingFramework\DataGenerator\Objects;
-use PHPUnit\Framework\TestCase;
+use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
+use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException;
+use tests\unit\Util\TestLoggingUtil;
/**
* The following function declarations override the global function_exists and declare msq/msqs for use
@@ -32,8 +34,17 @@ function msqs($id = null)
/**
* Class EntityDataObjectTest
*/
-class EntityDataObjectTest extends TestCase
+class EntityDataObjectTest extends MagentoTestCase
{
+ /**
+ * Before test functionality
+ * @return void
+ */
+ public function setUp()
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
public function testBasicGetters()
{
$data = ["datakey1" => "value1"];
@@ -77,12 +88,11 @@ public function testVarGetter()
$dataObject = new EntityDataObject("name", "type", $data, null, null, $vars);
// Perform Asserts
$this->assertEquals("id", $dataObject->getVarReference("someOtherEntity"));
-
}
public function testGetDataByNameInvalidUniquenessFormatValue()
{
- $this->expectException("Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException");
+ $this->expectException(TestFrameworkException::class);
$data = ["datakey1" => "value1", "datakey2" => "value2", "datakey3" => "value3"];
$dataObject = new EntityDataObject("name", "type", $data, null, null, null);
// Trigger Exception
@@ -92,7 +102,7 @@ public function testGetDataByNameInvalidUniquenessFormatValue()
public function testUniquenessFunctionsDontExist()
{
$this->markTestIncomplete('Test fails, as msqMock is always declared in test runs.');
- $this->expectException("Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException");
+ $this->expectException(TestFrameworkException::class);
$data = ["datakey1" => "value1", "datakey2" => "value2", "datakey3" => "value3"];
$uniquenessKeys = ["datakey1" => "suffix"];
$dataObject = new EntityDataObject("name", "type", $data, null, $uniquenessKeys, null);
@@ -109,4 +119,13 @@ public function testGetLinkedEntities()
$this->assertEquals("linkedEntity1", $dataObject->getLinkedEntitiesOfType("linkedEntityType")[0]);
$this->assertEquals("linkedEntity2", $dataObject->getLinkedEntitiesOfType("otherEntityType")[0]);
}
+
+ /**
+ * After class functionality
+ * @return void
+ */
+ public static function tearDownAfterClass()
+ {
+ TestLoggingUtil::getInstance()->clearMockLoggingUtil();
+ }
}
diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php
index 4b57a2f73..9e425e020 100644
--- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php
+++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php
@@ -9,12 +9,13 @@
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler;
use Magento\FunctionalTestingFramework\DataGenerator\Persist\OperationDataArrayResolver;
-use PHPUnit\Framework\TestCase;
+use Magento\FunctionalTestingFramework\Util\MagentoTestCase;
use tests\unit\Util\EntityDataObjectBuilder;
use tests\unit\Util\OperationDefinitionBuilder;
use tests\unit\Util\OperationElementBuilder;
+use tests\unit\Util\TestLoggingUtil;
-class OperationDataArrayResolverTest extends TestCase
+class OperationDataArrayResolverTest extends MagentoTestCase
{
const NESTED_METADATA_EXPECTED_RESULT = ["parentType" => [
"name" => "Hopper",
@@ -35,6 +36,15 @@ class OperationDataArrayResolverTest extends TestCase
]
]];
+ /**
+ * Before test functionality
+ * @return void
+ */
+ public function setUp()
+ {
+ TestLoggingUtil::getInstance()->setMockLoggingUtil();
+ }
+
/**
* Test a basic metadata resolve between primitive values and a primitive data set
*