diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index edd654a7393126c61b877d35b36de9077316ec4d..a4218e92b3486db76afc61254553b914610d99c3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -278,15 +278,19 @@ class HelperTest extends \PHPUnit_Framework_TestCase ->method('getOptionsReadOnly') ->willReturn(false); + $firstExpectedCustomOption = clone $this->customOptionMock; + $firstExpectedCustomOption->setData($optionsData['option2']); + $secondExpectedCustomOption = clone $this->customOptionMock; + $secondExpectedCustomOption->setData($optionsData['option3']); $this->customOptionFactoryMock->expects($this->any()) ->method('create') ->willReturnMap([ [ ['data' => $optionsData['option2']], - (clone $this->customOptionMock)->setData($optionsData['option2']) + $firstExpectedCustomOption ], [ ['data' => $optionsData['option3']], - (clone $this->customOptionMock)->setData($optionsData['option3']) + $secondExpectedCustomOption ] ]); diff --git a/app/code/Magento/Indexer/Setup/RecurringData.php b/app/code/Magento/Indexer/Setup/RecurringData.php new file mode 100644 index 0000000000000000000000000000000000000000..bd6f8f0241dc81cf5a632a355f5193c82e180023 --- /dev/null +++ b/app/code/Magento/Indexer/Setup/RecurringData.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Setup; + +use Magento\Framework\Setup\InstallDataInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Indexer\Model\IndexerFactory; +use Magento\Framework\Indexer\ConfigInterface; + +/** + * Recurring data upgrade for indexer module + */ +class RecurringData implements InstallDataInterface +{ + /** + * @var IndexerFactory + */ + private $indexerFactory; + + /** + * @var ConfigInterface + */ + private $configInterface; + + /** + * RecurringData constructor. + * + * @param IndexerFactory $indexerFactory + * @param ConfigInterface $configInterface + */ + public function __construct( + IndexerFactory $indexerFactory, + ConfigInterface $configInterface + ) { + $this->indexerFactory = $indexerFactory; + $this->configInterface = $configInterface; + } + + /** + * {@inheritdoc} + */ + public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + foreach (array_keys($this->configInterface->getIndexers()) as $indexerId) { + $indexer = $this->indexerFactory->create()->load($indexerId); + if ($indexer->isScheduled()) { + $indexer->getView()->unsubscribe()->subscribe(); + } + } + } +} diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index 1e1d217715d260e5f43c437b7a91431215d338f1..4b656ee299dddc04527d2be6145b49e3098c3578 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -602,7 +602,9 @@ abstract class AbstractProduct extends \Magento\Rule\Model\Condition\AbstractCon */ public function getMappedSqlField() { - if (!$this->isAttributeSetOrCategory()) { + if ($this->getAttribute() == 'sku') { + $mappedSqlField = 'e.sku'; + } elseif (!$this->isAttributeSetOrCategory()) { $mappedSqlField = $this->getEavAttributeTableAlias() . '.value'; } elseif ($this->getAttribute() == 'category_ids') { $mappedSqlField = 'e.entity_id'; diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml index da44fd2eaf99e55e925d9c946b6d7e8ee8370408..58435b066261ed28cc7f33ff01fc250940b6eb17 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml @@ -110,7 +110,7 @@ <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertConfigurableProductPage" /> </variation> <variation name="CreateConfigurableProductEntityTestVariation6" summary="Create Configurable Product with Creating New Category and New Attribute (Required Fields Only)" ticketId="MAGETWO-13361"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, stable:no</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data> <data name="product/data/configurable_attributes_data/dataset" xsi:type="string">two_searchable_options</data> <data name="product/data/name" xsi:type="string">Configurable Product %isolation%</data> <data name="product/data/sku" xsi:type="string">configurable_sku_%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php b/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php index 43c101176bdac80f4540c013f13b58d9b4a600d9..1314c1503b2f310db1ff80b461b05de986962935 100644 --- a/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php +++ b/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php @@ -74,7 +74,7 @@ class UpgradeSystemTest extends Injectable ); $version = $upgrade['upgradeVersion']; - $suffix = "( (CE|EE))$"; + $suffix = "( (CE|EE|B2B))$"; $normalVersion = '(0|[1-9]\d*)'; $preReleaseVersion = "((0(?!\\d+(\\.|\\+|{$suffix}))|[1-9A-Za-z])[0-9A-Za-z-]*)"; $buildVersion = '([0-9A-Za-z][0-9A-Za-z-]*)'; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php index 4029454ee523052fedcc9592b778fea9704b7abf..87bd329ef3b5611475c6ee9486cdc9f27c4ea5fe 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php @@ -22,6 +22,16 @@ class ModuleDBChangeTest extends \PHPUnit_Framework_TestCase */ protected static $changedFileList = ''; + /** + * @var string Path for Magento's composer.json + */ + protected static $composerFilePath = BP . '/composer.json'; + + /** + * @var bool Is tests executes on develop branch + */ + protected static $isOnDevVersion = false; + /** * Set changed files paths and list for all projects */ @@ -30,6 +40,13 @@ class ModuleDBChangeTest extends \PHPUnit_Framework_TestCase foreach (glob(self::$changedFilesPattern) as $changedFile) { self::$changedFileList .= file_get_contents($changedFile) . PHP_EOL; } + + if (file_exists(self::$composerFilePath)) { + $jsonData = json_decode(file_get_contents(self::$composerFilePath)); + if (substr((string) $jsonData->version, -4) == '-dev') { + self::$isOnDevVersion = true; + } + } } /** @@ -37,6 +54,9 @@ class ModuleDBChangeTest extends \PHPUnit_Framework_TestCase */ public function testModuleXmlFiles() { + if (self::$isOnDevVersion) { + $this->markTestSkipped('This test isn\'t applicable to the developer version of Magento'); + } preg_match_all('|etc/module\.xml$|mi', self::$changedFileList, $matches); $this->assertEmpty( reset($matches), @@ -50,6 +70,9 @@ class ModuleDBChangeTest extends \PHPUnit_Framework_TestCase */ public function testModuleSetupFiles() { + if (self::$isOnDevVersion) { + $this->markTestSkipped('This test isn\'t applicable to the developer version of Magento'); + } preg_match_all('|app/code/Magento/[^/]+/Setup/[^/]+$|mi', self::$changedFileList, $matches); $this->assertEmpty( reset($matches), diff --git a/lib/internal/Magento/Framework/Data/Argument/Interpreter/ArrayType.php b/lib/internal/Magento/Framework/Data/Argument/Interpreter/ArrayType.php index 74a6cc7d2cd923e46b80f39c2645c5cef2ba421a..d714caa1724f07c3aab05de714048e8ec72a6a80 100644 --- a/lib/internal/Magento/Framework/Data/Argument/Interpreter/ArrayType.php +++ b/lib/internal/Magento/Framework/Data/Argument/Interpreter/ArrayType.php @@ -54,25 +54,70 @@ class ArrayType implements InterpreterInterface */ private function sortItems($items) { - uasort( - $items, - function ($firstItem, $secondItem) { - $firstValue = 0; - $secondValue = 0; - if (isset($firstItem['sortOrder'])) { - $firstValue = intval($firstItem['sortOrder']); + $sortOrderDefined = $this->isSortOrderDefined($items); + if ($sortOrderDefined) { + $indexedItems = []; + foreach ($items as $key => $item) { + $indexedItems[] = ['key' => $key, 'item' => $item]; + } + uksort( + $indexedItems, + function ($firstItemKey, $secondItemKey) use ($indexedItems) { + return $this->compareItems($firstItemKey, $secondItemKey, $indexedItems); } + ); + // Convert array of sorted items back to initial format + $items = []; + foreach ($indexedItems as $indexedItem) { + $items[$indexedItem['key']] = $indexedItem['item']; + } + } + return $items; + } - if (isset($secondItem['sortOrder'])) { - $secondValue = intval($secondItem['sortOrder']); - } + /** + * Compare sortOrder of item + * + * @param mixed $firstItemKey + * @param mixed $secondItemKey + * @param array $indexedItems + * @return int + */ + private function compareItems($firstItemKey, $secondItemKey, $indexedItems) + { + $firstItem = $indexedItems[$firstItemKey]['item']; + $secondItem = $indexedItems[$secondItemKey]['item']; + $firstValue = 0; + $secondValue = 0; + if (isset($firstItem['sortOrder'])) { + $firstValue = intval($firstItem['sortOrder']); + } - if ($firstValue == $secondValue) { - return 0; - } - return $firstValue < $secondValue ? -1 : 1; + if (isset($secondItem['sortOrder'])) { + $secondValue = intval($secondItem['sortOrder']); + } + + if ($firstValue == $secondValue) { + // These keys reflect initial relative position of items. + // Allows stable sort for items with equal 'sortOrder' + return $firstItemKey < $secondItemKey ? -1 : 1; + } + return $firstValue < $secondValue ? -1 : 1; + } + + /** + * Determine if a sort order exists for any of the items. + * + * @param array $items + * @return bool + */ + private function isSortOrderDefined($items) + { + foreach ($items as $itemData) { + if (isset($itemData['sortOrder'])) { + return true; } - ); - return $items; + } + return false; } } diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/ArrayTypeTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/ArrayTypeTest.php index 7d3c21ba271cecf75a83d4bc9aa5923ce1920337..e1bc7528cbe1a00a0d5abebb2739cf9a83c7f43a 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/ArrayTypeTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/ArrayTypeTest.php @@ -103,6 +103,40 @@ class ArrayTypeTest extends \PHPUnit_Framework_TestCase 'key1' => '-value 1-', ], ], + 'pre-sorted array items' => [ + [ + 'item' => [ + 'key1' => ['value' => 'value 1'], + 'key4' => ['value' => 'value 4'], + 'key2' => ['value' => 'value 2', 'sortOrder' => 10], + 'key3' => ['value' => 'value 3'], + ], + ], + [ + 'key1' => '-value 1-', + 'key4' => '-value 4-', + 'key3' => '-value 3-', + 'key2' => '-value 2-', + ], + ], + 'sort order edge case values' => [ + [ + 'item' => [ + 'key1' => ['value' => 'value 1', 'sortOrder' => 101], + 'key4' => ['value' => 'value 4'], + 'key2' => ['value' => 'value 2', 'sortOrder' => -10], + 'key3' => ['value' => 'value 3'], + 'key5' => ['value' => 'value 5', 'sortOrder' => 20], + ], + ], + [ + 'key2' => '-value 2-', + 'key4' => '-value 4-', + 'key3' => '-value 3-', + 'key5' => '-value 5-', + 'key1' => '-value 1-', + ], + ], ]; } } diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php index cd7a236f723590ffe5138fa063dabfa5968c8c3b..2cbf5da0bfbbba3ab562942c068913f5d0f12069 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php @@ -40,13 +40,13 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, [], [], '', false); - $this->resourceMock = $this->getMock( - \Magento\Framework\App\ResourceConnection::class, - [], - [], - '', - false, - false + $this->resourceMock = $this->getMock( + \Magento\Framework\App\ResourceConnection::class, + [], + [], + '', + false, + false ); $this->connectionMock->expects($this->any()) @@ -57,19 +57,19 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('getConnection') ->willReturn($this->connectionMock); - $this->triggerFactoryMock = $this->getMock( + $this->triggerFactoryMock = $this->getMock( \Magento\Framework\DB\Ddl\TriggerFactory::class, [], [], '', false, false ); - $this->viewCollectionMock = $this->getMockForAbstractClass( + $this->viewCollectionMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\CollectionInterface::class, [], '', false, false, true, [] ); - $this->viewMock = $this->getMockForAbstractClass( + $this->viewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $this->resourceMock->expects($this->any()) ->method('getTableName') - ->willReturn($this->tableName); + ->will($this->returnArgument(0)); $this->model = new Subscription( $this->resourceMock, @@ -96,11 +96,15 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase $this->assertEquals('columnName', $this->model->getColumnName()); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testCreate() { $triggerName = 'trigger_name'; $this->resourceMock->expects($this->atLeastOnce())->method('getTriggerName')->willReturn($triggerName); $triggerMock = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Trigger::class) + ->setMethods(['setName', 'getName', 'setTime', 'setEvent', 'setTable', 'addStatement']) ->disableOriginalConstructor() ->getMock(); $triggerMock->expects($this->exactly(3)) @@ -121,11 +125,38 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('setTable') ->with($this->tableName) ->will($this->returnSelf()); - $triggerMock->expects($this->exactly(6)) + + $triggerMock->expects($this->at(4)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(5)) + ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(11)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(12)) + ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(18)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (OLD.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(19)) ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (OLD.columnName);") ->will($this->returnSelf()); - $changelogMock = $this->getMockForAbstractClass( + $changelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $changelogMock->expects($this->exactly(3)) @@ -143,7 +174,7 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('create') ->will($this->returnValue($triggerMock)); - $otherChangelogMock = $this->getMockForAbstractClass( + $otherChangelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $otherChangelogMock->expects($this->exactly(3)) @@ -153,7 +184,7 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('getColumnName') ->will($this->returnValue('entity_id')); - $otherViewMock = $this->getMockForAbstractClass( + $otherViewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $otherViewMock->expects($this->exactly(1)) @@ -216,7 +247,7 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('create') ->will($this->returnValue($triggerMock)); - $otherChangelogMock = $this->getMockForAbstractClass( + $otherChangelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $otherChangelogMock->expects($this->exactly(3)) @@ -226,7 +257,7 @@ class SubscriptionTest extends \PHPUnit_Framework_TestCase ->method('getColumnName') ->will($this->returnValue('entity_id')); - $otherViewMock = $this->getMockForAbstractClass( + $otherViewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $otherViewMock->expects($this->exactly(1)) diff --git a/lib/internal/Magento/Framework/Mview/etc/mview.xsd b/lib/internal/Magento/Framework/Mview/etc/mview.xsd index d171699c3784a8437985b6d741b0bc5d8297373a..0521691e852368d1e157733ff31e71c3ea41a07f 100644 --- a/lib/internal/Magento/Framework/Mview/etc/mview.xsd +++ b/lib/internal/Magento/Framework/Mview/etc/mview.xsd @@ -106,7 +106,7 @@ <xs:simpleType name="subscriptionModelType"> <xs:annotation> <xs:documentation> - Subscription model must be a valid PHP class or interface name. + DEPRECATED. Subscription model must be a valid PHP class or interface name. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string">