From db1ce7bc6aa48be45219bd55f524aa3473778d92 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 23 May 2018 13:54:05 -0500 Subject: [PATCH] MQE-999: Replace all explicit print or echo statements with logging - add LogginUtil and monolog - remove expliclit echo/print statements - update tests - update mftf exceptions to include log statments --- composer.json | 1 + composer.lock | 78 +++++++++++++++ .../Objects/EntityDataObjectTest.php | 19 ++++ .../OperationDataArrayResolverTest.php | 19 ++++ .../Suite/SuiteGeneratorTest.php | 30 +++++- .../Test/Objects/ActionGroupObjectTest.php | 19 ++++ .../Test/Objects/ActionObjectTest.php | 31 +++++- .../Test/Util/ActionMergeUtilTest.php | 18 ++++ .../Test/Util/ActionObjectExtractorTest.php | 46 ++++++--- .../Test/Util/ObjectExtensionUtilTest.php | 83 +++++++++++++--- .../Util/ModuleResolverTest.php | 32 ++++++- dev/tests/unit/Util/TestLoggingUtil.php | 94 +++++++++++++++++++ .../Tests/SuiteGenerationTest.php | 46 +++++++-- .../Objects/EntityDataObject.php | 24 +++-- .../Exceptions/TestReferenceException.php | 12 ++- .../Exceptions/XmlException.php | 12 ++- .../Suite/SuiteGenerator.php | 19 ++-- .../Test/Objects/ActionObject.php | 39 +++++--- .../Test/Util/ActionObjectExtractor.php | 24 ++--- .../Test/Util/ObjectExtensionUtil.php | 29 +++--- .../Util/Logger/LoggingUtil.php | 82 ++++++++++++++++ .../Util/ModuleResolver.php | 40 ++++++-- .../Util/TestGenerator.php | 12 +-- 23 files changed, 690 insertions(+), 119 deletions(-) create mode 100644 dev/tests/unit/Util/TestLoggingUtil.php create mode 100644 src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php diff --git a/composer.json b/composer.json index 0b88b59e3..8099cd58e 100755 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">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" diff --git a/composer.lock b/composer.lock index 8b13ba39a..fd17255c6 100644 --- a/composer.lock +++ b/composer.lock @@ -1618,6 +1618,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", diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php index 1b611dba6..ab6d52135 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Objects/EntityDataObjectTest.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; /** * The following function declarations override the global function_exists and declare msq/msqs for use @@ -34,6 +35,15 @@ function msqs($id = null) */ class EntityDataObjectTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + public function testBasicGetters() { $data = ["datakey1" => "value1"]; @@ -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..ea873c2e9 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Persist/OperationDataArrayResolverTest.php @@ -13,6 +13,7 @@ use tests\unit\Util\EntityDataObjectBuilder; use tests\unit\Util\OperationDefinitionBuilder; use tests\unit\Util\OperationElementBuilder; +use tests\unit\Util\TestLoggingUtil; class OperationDataArrayResolverTest extends TestCase { @@ -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 * @@ -344,4 +354,13 @@ public function testNestedMetadataArrayOfValue() // Do assert on result here $this->assertEquals(self::NESTED_METADATA_ARRAY_RESULT, $result); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index d6790f00e..a45ec9f32 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase; use tests\unit\Util\SuiteDataArrayBuilder; use tests\unit\Util\TestDataArrayBuilder; +use tests\unit\Util\TestLoggingUtil; class SuiteGeneratorTest extends TestCase { @@ -34,6 +35,15 @@ public static function setUpBeforeClass() ]); } + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests generating a single suite given a set of parsed test data * @throws \Exception @@ -63,7 +73,11 @@ public function testGenerateSuite() $mockSuiteGenerator->generateSuite("basicTestSuite"); // assert that expected suite is generated - $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ); } /** @@ -96,7 +110,11 @@ public function testGenerateAllSuites() $mockSuiteGenerator->generateAllSuites($exampleTestManifest); // assert that expected suites are generated - $this->expectOutputString("Suite basicTestSuite generated to _generated/basicTestSuite." . PHP_EOL); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => 'basicTestSuite', 'relative_path' => "_generated/basicTestSuite"] + ); } /** @@ -181,4 +199,12 @@ private function setMockTestAndSuiteParserOutput($testData, $suiteData) $property->setAccessible(true); $property->setValue($instance, $instance); } + + /** + * clean up function runs after all tests + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php index 8a5ba7100..a057ba074 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionGroupObjectTest.php @@ -18,11 +18,21 @@ use PHPUnit\Framework\TestCase; use tests\unit\Util\ActionGroupObjectBuilder; use tests\unit\Util\EntityDataObjectBuilder; +use tests\unit\Util\TestLoggingUtil; class ActionGroupObjectTest extends TestCase { const ACTION_GROUP_MERGE_KEY = 'TestKey'; + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests a string literal in an action group */ @@ -284,4 +294,13 @@ private function assertOnMergeKeyAndActionValue($actions, $expectedValue, $expec $this->assertEquals($expectedMergeKey, $action->getStepKey()); $this->assertEquals($expectedValue, $action->getCustomActionAttributes()); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index 8e3a1d579..35339eb93 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -16,12 +16,22 @@ use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Page\Objects\SectionObject; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; /** * Class ActionObjectTest */ class ActionObjectTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * The order offset should be 0 when the action is instantiated with 'before' */ @@ -234,14 +244,16 @@ public function testResolveUrlWithNoAttribute() )->make(); // bypass the private constructor AspectMock::double(PageObjectHandler::class, ['getInstance' => $instance]); - // Expect this warning to get generated - $this->expectOutputString( - "WARNING: Page url attribute not found for {{PageObject}} and is required for amOnPage." . PHP_EOL - ); - // Call the method under test $actionObject->resolveReferences(); + // Expect this warning to get generated + TestLoggingUtil::getInstance()->validateMockLogStatement( + "warning", + "page url attribute not found and is required", + ['action' => $actionObject->getType(), 'url' => '{{PageObject}}', 'stepKey' => $actionObject->getStepKey()] + ); + // Verify $expected = [ 'url' => '{{PageObject}}' @@ -371,4 +383,13 @@ private function mockDataHandlerWithData($dataObject) ->make(); AspectMock::double(DataObjectHandler::class, ['getInstance' => $dataInstance]); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php index bae9965dd..5bec686da 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php @@ -13,9 +13,19 @@ use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use PHPUnit\Framework\TestCase; use tests\unit\Util\DataObjectHandlerReflectionUtil; +use tests\unit\Util\TestLoggingUtil; class ActionMergeUtilTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Test to validate actions are properly ordered during a merge. * @@ -161,6 +171,14 @@ public function testInsertWait() 0 ); $this->assertEquals($expected, $actual); + } + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php index 873e622f7..f52d279eb 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php @@ -8,6 +8,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; class ActionObjectExtractorTest extends TestCase { @@ -20,6 +21,7 @@ class ActionObjectExtractorTest extends TestCase public function setUp() { $this->testActionObjectExtractor = new ActionObjectExtractor(); + TestLoggingUtil::getInstance()->setMockLoggingUtil(); } /** @@ -42,19 +44,27 @@ public function testBasicActionObjectExtration() public function testInvalidMergeOrderReference() { $invalidArray = $this->createBasicActionObjectArray('invalidTestAction1', 'invalidTestAction1'); - $this->expectException('\Magento\FunctionalTestingFramework\Exceptions\TestReferenceException'); - $expectedExceptionMessage = "Invalid ordering configuration in test TestWithSelfReferencingStepKey with step" . - " key(s):\n\tinvalidTestAction1\n"; - $this->expectExceptionMessage($expectedExceptionMessage); - - $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); + try { + $this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey'); + } catch (\Exception $e) { + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'error', + 'Line 103: Invalid ordering configuration in test', + [ + 'test' => 'TestWithSelfReferencingStepKey', + 'stepKey' => ['invalidTestAction1'] + ] + ); + + throw $e; + } } /** * Validates a warning is printed to the console when multiple actions reference the same actions for merging. */ - public function testAmbiguousMergeOrderRefernece() + public function testAmbiguousMergeOrderReference() { $ambiguousArray = $this->createBasicActionObjectArray('testAction1'); $ambiguousArray = array_merge( @@ -67,12 +77,16 @@ public function testAmbiguousMergeOrderRefernece() $this->createBasicActionObjectArray('testAction3', null, 'testAction1') ); - $outputString = "multiple actions referencing step key testAction1 in test AmbiguousRefTest:\n" . - "\ttestAction2\n" . - "\ttestAction3\n"; - - $this->expectOutputString($outputString); $this->testActionObjectExtractor->extractActions($ambiguousArray, 'AmbiguousRefTest'); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'warning', + 'multiple actions referencing step key', + [ + 'test' => 'AmbiguousRefTest', + 'stepKey' => 'testAction1', + 'ref' => ['testAction2', 'testAction3'] + ] + ); } /** @@ -103,4 +117,12 @@ private function createBasicActionObjectArray($stepKey = 'testAction1', $before return $baseArray; } + + /** + * clean up function runs after all tests + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php index ddddf6d4b..4b350e5a6 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ObjectExtensionUtilTest.php @@ -5,6 +5,7 @@ */ namespace tests\unit\Magento\FunctionalTestFramework\Test\Util; +use AspectMock\Proxy\Verifier; use AspectMock\Test as AspectMock; use Magento\FunctionalTestingFramework\ObjectManager\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; @@ -12,11 +13,24 @@ use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Test\Parsers\ActionGroupDataParser; use Magento\FunctionalTestingFramework\Test\Parsers\TestDataParser; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Monolog\Handler\TestHandler; +use Monolog\Logger; use PHPUnit\Framework\TestCase; use tests\unit\Util\TestDataArrayBuilder; +use tests\unit\Util\TestLoggingUtil; class ObjectExtensionUtilTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * Tests generating a test that extends another test * @throws \Exception @@ -41,11 +55,16 @@ public function testGenerateExtendedTest() $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); - // parse and generate test object with mocked data $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + // assert log statement is correct + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending test', + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + // assert that expected test is generated $this->assertEquals($testObject->getParentName(), "simpleTest"); $this->assertArrayHasKey("mockStep", $testObject->getOrderedActions()); @@ -79,11 +98,16 @@ public function testGenerateExtendedWithHooks() $mockTestData = ['tests' => array_merge($mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: simpleTest => extendedTest" . PHP_EOL); - // parse and generate test object with mocked data $testObject = TestObjectHandler::getInstance()->getObject('extendedTest'); + // assert log statement is correct + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending test', + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + // assert that expected test is generated $this->assertEquals($testObject->getParentName(), "simpleTest"); $this->assertArrayHasKey("mockStepBefore", $testObject->getHooks()['before']->getActions()); @@ -105,10 +129,15 @@ public function testExtendedTestNoParent() $mockTestData = ['tests' => array_merge($mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Parent Test simpleTest not defined for Test extendedTest. Skipping Test." . PHP_EOL); - // parse and generate test object with mocked data TestObjectHandler::getInstance()->getObject('extendedTest'); + + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + "parent test not defined. test will be skipped", + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); } /** @@ -137,11 +166,18 @@ public function testExtendingExtendedTest() $mockTestData = ['tests' => array_merge($mockParentTest, $mockSimpleTest, $mockExtendedTest)]; $this->setMockTestOutput($mockTestData); - $this->expectOutputString("Extending Test: anotherTest => simpleTest" . PHP_EOL); $this->expectExceptionMessage("Cannot extend a test that already extends another test. Test: simpleTest"); // parse and generate test object with mocked data TestObjectHandler::getInstance()->getObject('extendedTest'); + + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + "parent test not defined. test will be skipped", + ['parent' => 'simpleTest', 'test' => 'extendedTest'] + ); + $this->expectOutputString("Extending Test: anotherTest => simpleTest" . PHP_EOL); } /** @@ -186,11 +222,16 @@ public function testGenerateExtendedActionGroup() ]; $this->setMockTestOutput(null, $mockActionGroupData); - $this->expectOutputString("Extending Action Group: mockSimpleActionGroup => mockExtendedActionGroup" . PHP_EOL); - // parse and generate test object with mocked data $actionGroupObject = ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'debug', + 'extending action group:', + ['parent' => $mockSimpleActionGroup['name'], 'actionGroup' => $mockExtendedActionGroup['name']] + ); + // assert that expected test is generated $this->assertEquals("mockSimpleActionGroup", $actionGroupObject->getParentName()); $actions = $actionGroupObject->getActions(); @@ -266,13 +307,24 @@ public function testExtendingExtendedActionGroup() ]; $this->setMockTestOutput(null, $mockActionGroupData); - $this->expectOutputString("Extending Action Group: mockParentActionGroup => mockSimpleActionGroup" . PHP_EOL); $this->expectExceptionMessage( "Cannot extend an action group that already extends another action group. " . $mockSimpleActionGroup['name'] ); // parse and generate test object with mocked data - ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + try { + ActionGroupObjectHandler::getInstance()->getObject('mockExtendedActionGroup'); + } catch (\Exception $e) { + // validate log statement + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'error', + "Cannot extend an action group that already extends another action group. " . + $mockSimpleActionGroup['name'], + ['parent' => $mockSimpleActionGroup['name'], 'actionGroup' => $mockSimpleActionGroup['name']] + ); + + throw $e; + } } /** @@ -315,4 +367,13 @@ private function setMockTestOutput($testData = null, $actionGroupData = null) // bypass the private constructor AspectMock::double(ObjectManagerFactory::class, ['getObjectManager' => $instance]); } + + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } } diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php index ea670c4a6..0623d47a5 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Util/ModuleResolverTest.php @@ -11,11 +11,22 @@ use Magento\FunctionalTestingFramework\ObjectManager; use Magento\FunctionalTestingFramework\ObjectManagerFactory; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\ModuleResolver; use PHPUnit\Framework\TestCase; +use tests\unit\Util\TestLoggingUtil; class ModuleResolverTest extends TestCase { + /** + * Before test functionality + * @return void + */ + public function setUp() + { + TestLoggingUtil::getInstance()->setMockLoggingUtil(); + } + /** * remove all registered test doubles */ @@ -24,6 +35,15 @@ protected function tearDown() AspectMock::clean(); } + /** + * After class functionality + * @return void + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } + /** * Validate that Paths that are already set are returned * @throws \Exception @@ -91,10 +111,14 @@ public function testGetModulePathsLocations() public function testGetCustomModulePath() { $this->setMockResolverClass(false, ["Magento_TestModule"], null, null, [], ['otherPath']); - $this->expectOutputString("Including module path: otherPath" . PHP_EOL); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null, null); $this->assertEquals(['otherPath'], $resolver->getModulesPath()); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + 'including custom module', + ['module' => 'otherPath'] + ); } /** @@ -119,10 +143,14 @@ function ($arg1, $arg2) { return $mockValue; } ); - $this->expectOutputString("Excluding module: somePath" . PHP_EOL); $resolver = ModuleResolver::getInstance(); $this->setMockResolverProperties($resolver, null, null, ["somePath"]); $this->assertEquals(["otherPath", "lastPath"], $resolver->getModulesPath()); + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + 'excluding module', + ['module' => 'somePath'] + ); } /** diff --git a/dev/tests/unit/Util/TestLoggingUtil.php b/dev/tests/unit/Util/TestLoggingUtil.php new file mode 100644 index 000000000..eb73196d0 --- /dev/null +++ b/dev/tests/unit/Util/TestLoggingUtil.php @@ -0,0 +1,94 @@ +testLogHandler = new TestHandler(); + $testLogger = new Logger('testLogger'); + $testLogger->pushHandler($this->testLogHandler); + $mockLoggingUtil = AspectMock::double( + LoggingUtil::class, + ['getLogger' => $testLogger] + )->make(); + $property = new \ReflectionProperty(LoggingUtil::class, 'INSTANCE'); + $property->setAccessible(true); + $property->setValue($mockLoggingUtil); + } + + /** + * Function which validates messages have been logged as intended during test execution. + * + * @param string $type + * @param string $message + * @param array $context + */ + public function validateMockLogStatement($type, $message, $context) + { + $records = $this->testLogHandler->getRecords(); + $record = $records[count($records)-1]; // we assume the latest record is what requires validation + $this->assertEquals(strtoupper($type), $record['level_name']); + $this->assertEquals($message, $record['message']); + $this->assertEquals($context, $record['context']); + } + + /** + * Function which clears the test logger context from the LogginUtil class. Should be run after a test class has + * executed. + * + * @return void + */ + public function clearMockLoggingUtil() + { + AspectMock::clean(LoggingUtil::class); + } +} diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 4024329f1..0dd7017c0 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use PHPUnit\Util\Filesystem; use Symfony\Component\Yaml\Yaml; +use tests\unit\Util\TestLoggingUtil; use tests\util\MftfTestCase; class SuiteGenerationTest extends MftfTestCase @@ -52,6 +53,8 @@ public function setUp() realpath(FW_BP . '/etc/config/codeception.dist.yml'), self::CONFIG_YML_FILE ); + + TestLoggingUtil::getInstance()->setMockLoggingUtil(); } /** @@ -71,8 +74,13 @@ public function testSuiteGeneration1() // Generate the Suite SuiteGenerator::getInstance()->generateSuite($groupName); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); + self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -121,8 +129,14 @@ public function testSuiteGenerationParallel() $parallelManifest->createTestGroups(1); SuiteGenerator::getInstance()->generateAllSuites($parallelManifest); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message (for final group) and add group name for later deletion + $expectedGroup = $expectedGroups[count($expectedGroups)-1] ; + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $expectedGroup, 'relative_path' => "_generated/$expectedGroup"] + ); + self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -158,8 +172,12 @@ public function testSuiteGenerationHooks() // Generate the Suite SuiteGenerator::getInstance()->generateSuite($groupName); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -222,8 +240,12 @@ public function testSuiteGenerationSingleRun() SuiteGenerator::getInstance()->generateAllSuites($singleRunManifest); $singleRunManifest->generate(); - // Validate console message and add group name for later deletion - $this->expectOutputRegex('/Suite .* generated to .*/'); + // Validate log message and add group name for later deletion + TestLoggingUtil::getInstance()->validateMockLogStatement( + 'info', + "suite generated", + ['suite' => $groupName, 'relative_path' => "_generated/$groupName"] + ); self::$TEST_GROUPS[] = $groupName; // Validate Yaml file updated @@ -262,6 +284,14 @@ public function tearDown() ); } + /** + * Remove yml if created during tests and did not exist before + */ + public static function tearDownAfterClass() + { + TestLoggingUtil::getInstance()->clearMockLoggingUtil(); + } + /** * Getter for manifest file path * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php index d8564a26b..74e232145 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/EntityDataObject.php @@ -6,7 +6,9 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Objects; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class EntityDataObject @@ -126,10 +128,16 @@ public function getAllData() */ public function getDataByName($name, $uniquenessFormat) { + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(EntityDataObject::class) + ->debug("Fetching data field from entity", ["entity" => $this->getName(), "field" => $name]); + } + if (!$this->isValidUniqueDataFormat($uniquenessFormat)) { - throw new TestFrameworkException( - sprintf('Invalid unique data format value: %s \n', $uniquenessFormat) - ); + $exceptionMessage = sprintf("Invalid unique data format value: %s \n", $uniquenessFormat); + LoggingUtil::getInstance()->getLogger(EntityDataObject::class) + ->error($exceptionMessage, ["entity" => $this->getName(), "field" => $name]); + throw new TestFrameworkException($exceptionMessage); } $name_lower = strtolower($name); @@ -204,12 +212,12 @@ private function formatUniqueData($name, $uniqueData, $uniqueDataFormat) private function checkUniquenessFunctionExists($function, $uniqueDataFormat) { if (!function_exists($function)) { - throw new TestFrameworkException( - sprintf( - 'Unique data format value: %s can only be used when running cests.\n', - $uniqueDataFormat - ) + $exceptionMessage = sprintf( + 'Unique data format value: %s can only be used when running cests.\n', + $uniqueDataFormat ); + + throw new TestFrameworkException($exceptionMessage); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php index 1fd2e4f67..df871c75f 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/TestReferenceException.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Exceptions; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + /** * Class TestReferenceException */ @@ -14,9 +16,17 @@ class TestReferenceException extends \Exception /** * TestReferenceException constructor. * @param string $message + * @param array $context + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function __construct($message) + public function __construct($message, $context = []) { + list($childClass, $callingClass) = debug_backtrace(false, 2); + LoggingUtil::getInstance()->getLogger($callingClass['class'])->error( + "Line {$callingClass['line']}: $message", + $context + ); + parent::__construct($message); } } diff --git a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php index 2ee008b79..ac44e1885 100644 --- a/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php +++ b/src/Magento/FunctionalTestingFramework/Exceptions/XmlException.php @@ -6,6 +6,8 @@ namespace Magento\FunctionalTestingFramework\Exceptions; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; + /** * Class XmlException */ @@ -14,9 +16,17 @@ class XmlException extends \Exception /** * XmlException constructor. * @param string $message + * @param array $context + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function __construct($message) + public function __construct($message, $context = []) { + list($childClass, $callingClass) = debug_backtrace(false, 2); + LoggingUtil::getInstance()->getLogger($callingClass['class'])->error( + $message, + $context + ); + parent::__construct($message); } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index c7b31610a..4e0ef32df 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -13,6 +13,7 @@ use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\Manifest\BaseTestManifest; use Magento\FunctionalTestingFramework\Util\TestGenerator; use Symfony\Component\Yaml\Yaml; @@ -140,7 +141,10 @@ private function generateSuiteFromTest($suiteName, $tests = [], $originalSuiteNa $groupNamespace = $this->generateGroupFile($suiteName, $relevantTests, $originalSuiteName); $this->appendEntriesToConfig($suiteName, $fullPath, $groupNamespace); - print "Suite ${suiteName} generated to ${relativePath}.\n"; + LoggingUtil::getInstance()->getLogger(SuiteGenerator::class)->info( + "suite generated", + ['suite' => $suiteName, 'relative_path' => $relativePath] + ); } /** @@ -158,17 +162,12 @@ private function validateTestsReferencedInSuite($suiteName, $testsReferenced, $o { $suiteRef = $originalSuiteName ?? $suiteName; $possibleTestRef = SuiteObjectHandler::getInstance()->getObject($suiteRef)->getTests(); - $invalidTestRef = null; - $errorMsg = "Cannot reference tests not declared as part of {$suiteRef}:\n "; + $errorMsg = "Cannot reference tests whcih are not declared as part of suite."; - array_walk($testsReferenced, function ($value) use (&$invalidTestRef, $possibleTestRef, &$errorMsg) { - if (!array_key_exists($value, $possibleTestRef)) { - $invalidTestRef.= "\t{$value}\n"; - } - }); + $invalidTestRef = array_diff($testsReferenced, array_keys($possibleTestRef)); - if ($invalidTestRef != null) { - throw new TestReferenceException($errorMsg . $invalidTestRef); + if (!empty($invalidTestRef)) { + throw new TestReferenceException($errorMsg, ['suite' => $suiteRef, 'test' => $invalidTestRef]); } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index e2bd106d1..a7e3117da 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -15,6 +15,7 @@ use Magento\FunctionalTestingFramework\Page\Handlers\PageObjectHandler; use Magento\FunctionalTestingFramework\Page\Handlers\SectionObjectHandler; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ActionObject @@ -271,7 +272,10 @@ public function trimAssertionAttributes() // @codingStandardsIgnoreStart $appConfig = MftfApplicationConfig::getConfig(); if ($appConfig->getPhase() == MftfApplicationConfig::GENERATION_PHASE && $appConfig->verboseEnabled()) { - echo("WARNING: Use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax (Action: {$this->type} StepKey: {$this->stepKey})" . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + "use of one line Assertion actions will be deprecated in MFTF 3.0.0, please use nested syntax", + ["action" => $this->type, "stepKey" => $this->stepKey] + ); } // @codingStandardsIgnoreEnd return; @@ -318,7 +322,10 @@ private function validateAssertionSchema($attributes) if (!in_array('expectedResult', $attributes) || !in_array('actualResult', $attributes)) { // @codingStandardsIgnoreStart - throw new TestReferenceException("{$this->type} must have both an expectedResult and actualResult defined (stepKey: {$this->stepKey})"); + throw new TestReferenceException( + "{$this->type} must have both an expectedResult and actualResult defined (stepKey: {$this->stepKey})", + ["action" => $this->type, "stepKey" => $this->stepKey] + ); // @codingStandardsIgnoreEnd } } @@ -373,11 +380,12 @@ private function resolveUrlReference() if ($replacement) { $this->resolvedCustomAttributes[ActionObject::ACTION_ATTRIBUTE_URL] = $replacement; $allPages = PageObjectHandler::getInstance()->getAllObjects(); - if ( - $replacement === $url - && array_key_exists(trim($url, "{}"), $allPages) + if ($replacement === $url && array_key_exists(trim($url, "{}"), $allPages) ) { - echo("WARNING: Page url attribute not found for ${url} and is required for $this->type." . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ActionObject::class)->warning( + "page url attribute not found and is required", + ["action" => $this->type, "url" => $url, "stepKey" => $this->stepKey] + ); } } } @@ -505,7 +513,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) } elseif (get_class($obj) == SectionObject::class) { list(,$objField) = $this->stripAndSplitReference($match); if ($obj->getElement($objField) == null) { - throw new TestReferenceException("Could not resolve entity reference " . $inputString); + throw new TestReferenceException("Could not resolve entity reference", ["input" => $inputString]); } $parameterized = $obj->getElement($objField)->isParameterized(); $replacement = $obj->getElement($objField)->getPrioritizedSelector(); @@ -522,7 +530,7 @@ private function findAndReplaceReferences($objectHandler, $inputString) if (get_class($objectHandler) != DataObjectHandler::class) { return $this->findAndReplaceReferences(DataObjectHandler::getInstance(), $outputString); } else { - throw new TestReferenceException("Could not resolve entity reference " . $inputString); + throw new TestReferenceException("Could not resolve entity reference", ["input" => $inputString]); } } @@ -545,12 +553,14 @@ private function validateMutuallyExclusiveAttributes(array $attributes) if (count($matches) > 1) { throw new TestReferenceException( "Actions of type '{$this->getType()}' must only contain one attribute of types '" - . implode("', '", $attributes) . "'" + . implode("', '", $attributes) . "'", + ["type" => $this->getType(), "attributes" => $attributes] ); } elseif (count($matches) == 0) { throw new TestReferenceException( "Actions of type '{$this->getType()}' must contain at least one attribute of types '" - . implode("', '", $attributes) . "'" + . implode("', '", $attributes) . "'", + ["type" => $this->getType(), "attributes" => $attributes] ); } } @@ -567,7 +577,8 @@ private function validateUrlAreaAgainstActionType($obj) if ($obj->getArea() == 'external' && in_array($this->getType(), self::EXTERNAL_URL_AREA_INVALID_ACTIONS)) { throw new TestReferenceException( - "Page of type 'external' is not compatible with action type '{$this->getType()}'" + "Page of type 'external' is not compatible with action type '{$this->getType()}'", + ["type" => $this->getType()] ); } } @@ -640,12 +651,14 @@ private function matchParameterReferences($reference, $parameters) } throw new TestReferenceException( "Parameter Resolution Failed: Not enough parameters given for reference " . - $reference . ". Parameters Given: " . $parametersGiven + $reference . ". Parameters Given: " . $parametersGiven, + ["reference" => $reference, "parametersGiven" => $parametersGiven] ); } elseif (count($varMatches[0]) < count($parameters)) { throw new TestReferenceException( "Parameter Resolution Failed: Too many parameters given for reference " . - $reference . ". Parameters Given: " . implode(", ", $parameters) + $reference . ". Parameters Given: " . implode(", ", $parameters), + ["reference" => $reference, "parametersGiven" => $parameters] ); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php index 95c6f1501..4ffd70ecf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php @@ -11,6 +11,7 @@ use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ActionObjectExtractor @@ -243,12 +244,10 @@ private function auditMergeSteps($stepKeyRefs, $testName) }, ARRAY_FILTER_USE_BOTH); if (!empty($invalidStepRef)) { - $errorMsg = "Invalid ordering configuration in test {$testName} with step key(s):\n"; - array_walk($invalidStepRef, function ($value, $key) use (&$errorMsg) { - $errorMsg.="\t{$key}\n"; - }); - - throw new TestReferenceException($errorMsg); + throw new TestReferenceException( + "Invalid ordering configuration in test", + ['test' => $testName, 'stepKey' => array_keys($invalidStepRef)] + ); } // check for ambiguous references to step keys (multiple refs across test merges). @@ -256,16 +255,11 @@ private function auditMergeSteps($stepKeyRefs, $testName) return count($value) > 1; }); - $multipleActionsError = ""; foreach ($atRiskStepRef as $stepKey => $stepRefs) { - $multipleActionsError.= "multiple actions referencing step key {$stepKey} in test {$testName}:\n"; - array_walk($stepRefs, function ($value) use (&$multipleActionsError) { - $multipleActionsError.= "\t{$value}\n"; - }); - } - - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - print $multipleActionsError; + LoggingUtil::getInstance()->getLogger(ActionObjectExtractor::class)->warn( + 'multiple actions referencing step key', + ['test' => $testName, 'stepKey' => $stepKey, 'ref' => $stepRefs] + ); } } } diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php index 14a02739d..97f67004a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ObjectExtensionUtil.php @@ -15,6 +15,7 @@ use Magento\FunctionalTestingFramework\Test\Objects\TestObject; use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; use Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; class ObjectExtensionUtil { @@ -40,13 +41,10 @@ public function extendTest($testObject) $parentTest = TestObjectHandler::getInstance()->getObject($testObject->getParentName()); } catch (TestReferenceException $error) { if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo( - "Parent Test " . - $testObject->getParentName() . - " not defined for Test " . - $testObject->getName() . - ". Skipping Test." . - PHP_EOL); + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class)->debug( + "parent test not defined. test will be skipped", + ["parent" => $testObject->getParentName(), "test" => $testObject->getName()] + ); } $skippedTest = $this->skipTest($testObject); return $skippedTest; @@ -55,11 +53,13 @@ public function extendTest($testObject) // Check to see if the parent test is already an extended test if ($parentTest->getParentName() !== null) { throw new XmlException( - "Cannot extend a test that already extends another test. Test: " . $parentTest->getName() + "Cannot extend a test that already extends another test. Test: " . $parentTest->getName(), + ["parent" => $parentTest->getName(), "actionGroup" => $testObject->getName()] ); } if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo("Extending Test: " . $parentTest->getName() . " => " . $testObject->getName() . PHP_EOL); + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class) + ->debug("extending test", ["parent" => $parentTest->getName(), "test" => $testObject->getName()]); } // Get steps for both the parent and the child tests @@ -106,15 +106,14 @@ public function extendActionGroup($actionGroupObject) if ($parentActionGroup->getParentName() !== null) { throw new XmlException( "Cannot extend an action group that already extends another action group. " . - $parentActionGroup->getName() + $parentActionGroup->getName(), + ["parent" => $parentActionGroup->getName(), "actionGroup" => $actionGroupObject->getName()] ); } if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - echo("Extending Action Group: " . - $parentActionGroup->getName() . - " => " . - $actionGroupObject->getName() . - PHP_EOL + LoggingUtil::getInstance()->getLogger(ObjectExtensionUtil::class)->debug( + "extending action group:", + ["parent" => $parentActionGroup->getName(), "actionGroup" => $actionGroupObject->getName()] ); } diff --git a/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php new file mode 100644 index 000000000..51a4e5d8c --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/Logger/LoggingUtil.php @@ -0,0 +1,82 @@ +loggers)) { + $logger = new Logger($clazz); + $logger->pushHandler(new StreamHandler($this->getLoggingPath())); + $this->loggers[$clazz] = $logger; + } + + return $this->loggers[$clazz]; + } + + /** + * Function which returns a static path to the the log file. + * + * @return string + */ + private function getLoggingPath() + { + return TESTS_BP . DIRECTORY_SEPARATOR . "mftf.log"; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 6ab136a52..a32486aba 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -7,6 +7,7 @@ namespace Magento\FunctionalTestingFramework\Util; use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; /** * Class ModuleResolver, resolve module path based on enabled modules of target Magento instance. @@ -191,11 +192,13 @@ public function getModulesPath() $enabledModules = $this->getEnabledModules(); if (empty($enabledModules) && !MftfApplicationConfig::getConfig()->forceGenerateEnabled()) { - trigger_error( - "Could not retrieve enabled modules from provided 'MAGENTO_BASE_URL'," . - "please make sure Magento is available at this url", - E_USER_ERROR + $errorMsg = 'Could not retrieve enabled modules from provided MAGENTO_BASE_URL ' . + 'please make sure Magento is available at this url'; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->error( + $errorMsg, + ['MAGENTO_BASE_URL' => getenv('MAGENTO_BASE_URL')] ); + trigger_error($errorMsg, E_USER_ERROR); } $allModulePaths = $this->aggregateTestModulePaths(); @@ -270,6 +273,13 @@ private function globRelevantPaths($testPath, $pattern) foreach ($relevantPaths as $codePath) { $mainModName = basename(str_replace($pattern, '', $codePath)); $modulePaths[$mainModName][] = $codePath; + + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->debug( + "including module", + ['module' => $mainModName, 'path' => $codePath] + ); + } } return $modulePaths; @@ -337,7 +347,10 @@ private function printMagentoVersionInfo() return; } $url = ConfigSanitizerUtil::sanitizeUrl(getenv('MAGENTO_BASE_URL')) . $this->versionUrl; - print "Fetching version information from {$url}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "Fetching version information.", + ['url' => $url] + ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); @@ -348,9 +361,10 @@ private function printMagentoVersionInfo() $response = "No version information available."; } - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - print "\nVersion Information: {$response}\n"; - } + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + 'version information', + ['version' => $response] + ); } /** @@ -413,7 +427,10 @@ protected function applyCustomModuleMethods($modulesPath) $customModulePaths = $this->getCustomModulePaths(); array_map(function ($value) { - print "Including module path: {$value}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "including custom module", + ['module' => $value] + ); }, $customModulePaths); return $this->flattenAllModulePaths(array_merge($modulePathsResult, $customModulePaths)); @@ -431,7 +448,10 @@ private function removeBlacklistModules($modulePaths) foreach ($modulePathsResult as $moduleName => $modulePath) { if (in_array($moduleName, $this->getModuleBlacklist())) { unset($modulePathsResult[$moduleName]); - print "Excluding module: {$moduleName}\n"; + LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info( + "excluding module", + ['module' => $moduleName] + ); } } diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 789aa04f8..c55cfa916 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -16,6 +16,7 @@ use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\TestHookObject; use Magento\FunctionalTestingFramework\Test\Objects\TestObject; +use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; use Magento\FunctionalTestingFramework\Util\Manifest\BaseTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; @@ -130,11 +131,10 @@ private function loadAllTestObjects($testsToIgnore) // them in the current context. $invalidTestObjects = array_intersect_key($this->tests, $testsToIgnore); if (!empty($invalidTestObjects)) { - $errorMsg = "Cannot reference the following tests for generation without accompanying suite:\n"; - array_walk($invalidTestObjects, function ($value, $key) use (&$errorMsg) { - $errorMsg.= "\t{$key}\n"; - }); - throw new TestReferenceException($errorMsg); + throw new TestReferenceException( + "Cannot reference test configuration for generation without accompanying suite.", + ['tests' => array_keys($invalidTestObjects)] + ); } return $this->tests; @@ -155,7 +155,7 @@ private function createCestFile($testPhp, $filename) $file = fopen($exportFilePath, 'w'); if (!$file) { - throw new \Exception("Could not open the file!"); + throw new \Exception("Could not open the file."); } fwrite($file, $testPhp);