From 43ce9fc83cd2094fc476907b14c366a2195cd5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Mon, 6 Jan 2020 15:59:49 +0100 Subject: [PATCH 1/5] Introduce magentoCron command to execute Cron Jobs taking into consideration cron required interval (60 seconds) --- etc/di.xml | 2 +- .../Module/MagentoWebDriver.php | 110 ++++++++++++++++++ .../Test/etc/Actions/customActions.xsd | 37 +++++- .../Util/TestGenerator.php | 24 +++- 4 files changed, 170 insertions(+), 3 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index 3e6313bf3..e5e31cf87 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,7 +8,7 @@ + ]> diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index a95586b26..3baa93cea 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -53,6 +53,9 @@ class MagentoWebDriver extends WebDriver { use AttachmentSupport; + const MAGENTO_CRON_INTERVAL = 60; + const MAGENTO_CRON_COMMAND = 'cron:run'; + /** * List of known magento loading masks by selector * @@ -121,6 +124,13 @@ class MagentoWebDriver extends WebDriver */ private $jsErrors = []; + /** + * Contains last execution times for Cron + * + * @var int[] + */ + private $cronExecution = []; + /** * Sanitizes config, then initializes using parent. * @@ -552,6 +562,74 @@ public function magentoCLI($command, $timeout = null, $arguments = null) return $response; } + /** + * Executes Magento Cron keeping the interval (> 60 seconds between each run) + * + * @param string|null $cronGroups + * @param int|null $timeout + * @param string|null $arguments + */ + public function magentoCron($cronGroups = null, $timeout = null, $arguments = null) + { + $cronGroups = explode(' ', $cronGroups); + return $this->executeCronjobs($cronGroups, $timeout, $arguments); + } + + /** + * Updates last execution time for Cron + * + * @param array $cronGroups + * @return void + */ + private function notifyCronFinished(array $cronGroups = []) + { + if (empty($cronGroups)) { + $this->cronExecution['*'] = time(); + } + + foreach ($cronGroups as $group) { + $this->cronExecution[$group] = time(); + } + } + + /** + * Returns last Cron execution time for specific cron or all crons + * + * @param array $cronGroups + * @return int + */ + private function getLastCronExecution(array $cronGroups = []) + { + if (empty($cronGroups)) { + return (int)max($this->cronExecution); + } + + $cronGroups = array_merge($cronGroups, ['*']); + + return array_reduce($cronGroups, function($lastExecution, $group) { + if (isset($this->cronExecution[$group]) && $this->cronExecution[$group] > $lastExecution) { + $lastExecution = $this->cronExecution[$group]; + } + + return (int)$lastExecution; + }, 0); + } + + /** + * Returns time to wait for next run + * + * @param array $cronGroups + * @param int $cronInterval + * @return int + */ + private function getCronWait(array $cronGroups = [], int $cronInterval = self::MAGENTO_CRON_INTERVAL) + { + $nextRun = $this->getLastCronExecution($cronGroups) + $cronInterval; + $toNextRun = $nextRun - time(); + + return max(0, $toNextRun); + } + /** * Runs DELETE request to delete a Magento entity against the url given. * @@ -971,4 +1049,36 @@ public function getSecret($key) { return CredentialStore::getInstance()->getSecret($key); } + + /** + * Waits proper amount of time to perform Cron execution + * + * @param $cronGroups + * @param $timeout + * @param $arguments + * @return string + * @throws TestFrameworkException + */ + private function executeCronjobs($cronGroups, $timeout, $arguments): string + { + $cronGroups = array_filter($cronGroups); + + $waitFor = $this->getCronWait($cronGroups); + + if ($waitFor) { + $this->wait($waitFor); + } + + $command = array_reduce($cronGroups, function ($command, $cronGroup) { + $command .= ' --group=' . $cronGroup; + return $command; + }, self::MAGENTO_CRON_COMMAND); + $timeStart = microtime(true); + $cronResult = $this->magentoCLI($command, $timeout, $arguments); + $timeEnd = microtime(true); + + $this->notifyCronFinished($cronGroups); + + return sprintf('%s (wait: %ss, execution: %ss)', $cronResult, $waitFor, round($timeEnd - $timeStart, 2)); + } } diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd index 2424d0d31..8670d9885 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/Actions/customActions.xsd @@ -12,6 +12,7 @@ + @@ -62,6 +63,40 @@ + + + + Executes Magento Cron Jobs (selected groups) + + + + + + + + Cron groups to be executed (separated by space) + + + + + + + Arguments for Magento CLI command, will not be escaped. + + + + + + + Idle timeout in seconds, defaulted to 60s when not specified. + + + + + + + + @@ -285,4 +320,4 @@ - \ No newline at end of file + diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 09c353029..e98dcbe61 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -50,10 +50,12 @@ class TestGenerator 'retrieveEntityField', 'getSecret', 'magentoCLI', + 'magentoCron', 'generateDate', 'field' ]; const STEP_KEY_ANNOTATION = " // stepKey: %s"; + const CRON_INTERVAL = 60; /** * Actor name for AcceptanceTest @@ -534,6 +536,7 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato $dependentSelector = null; $visible = null; $command = null; + $cronGroups = ''; $arguments = null; $sortOrder = null; $storeCode = null; @@ -551,6 +554,9 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato if (isset($customActionAttributes['command'])) { $command = $this->addUniquenessFunctionCall($customActionAttributes['command']); } + if (isset($customActionAttributes['groups'])) { + $cronGroups = $this->addUniquenessFunctionCall($customActionAttributes['groups']); + } if (isset($customActionAttributes['arguments'])) { $arguments = $this->addUniquenessFunctionCall($customActionAttributes['arguments']); } @@ -1270,6 +1276,22 @@ public function generateStepsPhp($actionObjects, $generationScope = TestGenerato $stepKey ); break; + case 'magentoCron': + $testSteps .= $this->wrapFunctionCallWithReturnValue( + $stepKey, + $actor, + $actionObject, + $cronGroups, + self::CRON_INTERVAL + $time, + $arguments + ); + $testSteps .= sprintf(self::STEP_KEY_ANNOTATION, $stepKey) . PHP_EOL; + $testSteps .= sprintf( + "\t\t$%s->comment(\$%s);", + $actor, + $stepKey + ); + break; case "field": $fieldKey = $actionObject->getCustomActionAttributes()['key']; $input = $this->resolveStepKeyReferences($input, $actionObject->getActionOrigin()); @@ -1403,7 +1425,7 @@ private function replaceMatchesIntoArg($matches, &$outputArg) $variable = $this->stripAndSplitReference($match, $delimiter); if (count($variable) != 2) { throw new \Exception( - "Invalid Persisted Entity Reference: {$match}. + "Invalid Persisted Entity Reference: {$match}. Test persisted entity references must follow {$delimiter}entityStepKey.field{$delimiter} format." ); } From df058d78ed0b22eeb1d47352144c5a3828966d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Mon, 6 Jan 2020 16:12:02 +0100 Subject: [PATCH 2/5] Code Style like 80s! --- .../Module/MagentoWebDriver.php | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php index 3baa93cea..f44d7e1a0 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php @@ -565,9 +565,10 @@ public function magentoCLI($command, $timeout = null, $arguments = null) /** * Executes Magento Cron keeping the interval (> 60 seconds between each run) * - * @param string|null $cronGroups - * @param int|null $timeout - * @param string|null $arguments + * @param string|null $cronGroups + * @param integer|null $timeout + * @param string|null $arguments + * @return string */ public function magentoCron($cronGroups = null, $timeout = null, $arguments = null) { @@ -596,7 +597,7 @@ private function notifyCronFinished(array $cronGroups = []) * Returns last Cron execution time for specific cron or all crons * * @param array $cronGroups - * @return int + * @return integer */ private function getLastCronExecution(array $cronGroups = []) { @@ -606,7 +607,7 @@ private function getLastCronExecution(array $cronGroups = []) $cronGroups = array_merge($cronGroups, ['*']); - return array_reduce($cronGroups, function($lastExecution, $group) { + return array_reduce($cronGroups, function ($lastExecution, $group) { if (isset($this->cronExecution[$group]) && $this->cronExecution[$group] > $lastExecution) { $lastExecution = $this->cronExecution[$group]; } @@ -618,9 +619,9 @@ private function getLastCronExecution(array $cronGroups = []) /** * Returns time to wait for next run * - * @param array $cronGroups - * @param int $cronInterval - * @return int + * @param array $cronGroups + * @param integer $cronInterval + * @return integer */ private function getCronWait(array $cronGroups = [], int $cronInterval = self::MAGENTO_CRON_INTERVAL) { @@ -1053,9 +1054,9 @@ public function getSecret($key) /** * Waits proper amount of time to perform Cron execution * - * @param $cronGroups - * @param $timeout - * @param $arguments + * @param string $cronGroups + * @param integer $timeout + * @param string $arguments * @return string * @throws TestFrameworkException */ From 199391561191460956489c26d97afc39b83514a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Wed, 8 Jan 2020 23:52:06 +0100 Subject: [PATCH 3/5] Add tests to cover new feature --- .../verification/TestModule/Test/BasicFunctionalTest.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml index c23a3ce60..1ef25b43d 100644 --- a/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml +++ b/dev/tests/verification/TestModule/Test/BasicFunctionalTest.xml @@ -79,6 +79,9 @@ + + + @@ -143,4 +146,4 @@ - \ No newline at end of file + From f61897228498c1a2734bf75dd17891645c6fef4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Thu, 9 Jan 2020 00:02:38 +0100 Subject: [PATCH 4/5] Add tests to cover new feature --- dev/tests/verification/Resources/BasicFunctionalTest.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dev/tests/verification/Resources/BasicFunctionalTest.txt b/dev/tests/verification/Resources/BasicFunctionalTest.txt index 5bcfd55dd..e78f1b49c 100644 --- a/dev/tests/verification/Resources/BasicFunctionalTest.txt +++ b/dev/tests/verification/Resources/BasicFunctionalTest.txt @@ -126,6 +126,12 @@ class BasicFunctionalTestCest $I->comment($magentoCli3); // stepKey: magentoCli3 $magentoCli4 = $I->magentoCLISecret("config:set somePath " . $I->getSecret("someKey"), 120); // stepKey: magentoCli4 $I->comment($magentoCli4); // stepKey: magentoCli4 + $cronAllGroups = $I->magentoCron("", 70); // stepKey: cronAllGroups + $I->comment($cronAllGroups); + $cronSingleGroup = $I->magentoCron("index", 70); // stepKey: cronSingleGroup + $I->comment($cronSingleGroup); + $cronMultipleGroups = $I->magentoCron("a b c", 70); // stepKey: cronMultipleGroups + $I->comment($cronMultipleGroups); $I->makeScreenshot("screenShotInput"); // stepKey: makeScreenshotKey1 $I->maximizeWindow(); // stepKey: maximizeWindowKey1 $I->moveBack(); // stepKey: moveBackKey1 From f3bb5c2cfadd35ed6c8f50dc8fac402b69118ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Thu, 9 Jan 2020 00:18:02 +0100 Subject: [PATCH 5/5] Add documentation to `` command --- docs/test/actions.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/test/actions.md b/docs/test/actions.md index c5dc83fdb..59fd12aa2 100644 --- a/docs/test/actions.md +++ b/docs/test/actions.md @@ -1274,6 +1274,29 @@ Attribute|Type|Use|Description ``` +### magentoCron + +Used to execute Magento Cron jobs. Groups may be provided optionally. Internal mechanism of `` ensures that Cron Job of single group is ran with 60 seconds interval. + +Attribute|Type|Use|Description +---|---|---|--- +`groups`|string |optional| Run only specified groups of Cron Jobs +`arguments`|string |optional| Unescaped arguments to be passed in with the CLI command. +`timeout`|string|optional| Number of seconds CLI command can run without outputting anything. +`stepKey`|string|required| A unique identifier of the action. +`before`|string|optional| `stepKey` of action that must be executed next. +`after`|string|optional| `stepKey` of preceding action. + + +#### Example +```xml + + + + + +``` + ### makeScreenshot See [makeScreenshot docs on codeception.com](http://codeception.com/docs/modules/WebDriver#makeScreenshot).