diff --git a/app/code/Magento/Backend/Helper/Dashboard/Order.php b/app/code/Magento/Backend/Helper/Dashboard/Order.php
index e3c5dfbb4b9450e718600e191c3536b48ad7be9e..d8ece48d182e409baaed64ffe98bca31ce71360c 100644
--- a/app/code/Magento/Backend/Helper/Dashboard/Order.php
+++ b/app/code/Magento/Backend/Helper/Dashboard/Order.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Backend\Helper\Dashboard;
 
+use Magento\Framework\App\ObjectManager;
+
 /**
  * Adminhtml dashboard helper for orders
  */
@@ -23,18 +25,24 @@ class Order extends \Magento\Backend\Helper\Dashboard\AbstractDashboard
     /**
      * @param \Magento\Framework\App\Helper\Context $context
      * @param \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection
-     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      */
     public function __construct(
         \Magento\Framework\App\Helper\Context $context,
-        \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection,
-        \Magento\Store\Model\StoreManagerInterface $storeManager
+        \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection
     ) {
         $this->_orderCollection = $orderCollection;
-        $this->_storeManager = $storeManager;
-        parent::__construct(
-            $context
-        );
+        parent::__construct($context);
+    }
+
+    /**
+     * @return \Magento\SalesRule\Model\RuleFactory
+     * @deprecated
+     */
+    public function getStoreManager()
+    {
+        if ($this->_storeManager instanceof \Magento\Store\Model\StoreManagerInterface) {
+            $this->_storeManager = ObjectManager::getInstance()->get('\Magento\Store\Model\StoreManagerInterface');
+        }
     }
 
     /**
diff --git a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml
index 534b7e88192010cb170946a7c31009fbd16d92cc..378e72e42d8ab582d5b97a9af035c9198b46a863 100644
--- a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml
+++ b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml
@@ -27,7 +27,19 @@
     </form>
     <script data-template="search-suggest" type="text/x-magento-template">
         <ul class="search-global-menu">
-        <% if (data.items.length) { %>
+        <li class="item">
+            <a href="<?php /* @escapeNotVerified */ echo $block->getURL('catalog/product/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Products</a>
+        </li>
+        <li class="item">
+            <a href="<?php /* @escapeNotVerified */ echo $block->getURL('sales/order/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Orders</a>
+        </li>
+        <li class="item">
+            <a href="<?php /* @escapeNotVerified */ echo $block->getURL('customer/index/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Customers</a>
+        </li>
+        <li class="item">
+            <a href="<?php /* @escapeNotVerified */ echo $block->getURL('cms/page/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Pages</a>
+        </li>
+            <% if (data.items.length) { %>
             <% _.each(data.items, function(value){ %>
             <li class="item"
                 <%- data.optionData(value) %>
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php
index 9ba1ad0b6436f3b4a921c6e212f6acd2890ef188..daa08141549d77de4300d00e320e4b3c13e6057b 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php
@@ -100,7 +100,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category
 
         $data['general'] = $this->getRequest()->getPostValue();
         $isNewCategory = !isset($data['general']['entity_id']);
-        $data = $this->stringToBoolConverting($this->stringToBoolInputs, $data);
+        $data = $this->stringToBoolConverting($data);
         $data = $this->imagePreprocessing($data);
         $storeId = isset($data['general']['store_id']) ? $data['general']['store_id'] : null;
         $parentId = isset($data['general']['parent']) ? $data['general']['parent'] : null;
@@ -121,11 +121,9 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category
                 foreach ($generalPost['use_config'] as $attributeCode => $attributeValue) {
                     if ($attributeValue) {
                         $useConfig[] = $attributeCode;
+                        $category->setData($attributeCode, null);
                     }
                 }
-                foreach ($useConfig as $attributeCode) {
-                    $category->setData($attributeCode, null);
-                }
             }
 
             $category->setAttributeSetId($category->getDefaultAttributeSetId());
@@ -148,7 +146,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category
             if (isset($generalPost['use_default']) && !empty($generalPost['use_default'])) {
                 foreach ($generalPost['use_default'] as $attributeCode => $attributeValue) {
                     if ($attributeValue) {
-                        $category->setData($attributeCode, false);
+                        $category->setData($attributeCode, null);
                     }
                 }
             }
@@ -234,8 +232,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category
     {
         if (!isset($_FILES) || (isset($_FILES['image']) && $_FILES['image']['name'] === '' )) {
             unset($data['general']['image']);
-            if (
-                isset($data['general']['savedImage']['delete']) &&
+            if (isset($data['general']['savedImage']['delete']) &&
                 $data['general']['savedImage']['delete']
             ) {
                 $data['general']['image']['delete'] = $data['general']['savedImage']['delete'];
@@ -247,17 +244,20 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category
     /**
      * Converting inputs from string to boolean
      *
-     * @param array $stringToBoolInputs
      * @param array $data
+     * @param array $stringToBoolInputs
      *
      * @return array
      */
-    public function stringToBoolConverting($stringToBoolInputs, $data)
+    public function stringToBoolConverting($data, $stringToBoolInputs = null)
     {
+        if (null === $stringToBoolInputs) {
+            $stringToBoolInputs = $this->stringToBoolInputs;
+        }
         foreach ($stringToBoolInputs as $key => $value) {
             if (is_array($value)) {
                 if (isset($data[$key])) {
-                    $data[$key] = $this->stringToBoolConverting($value, $data[$key]);
+                    $data[$key] = $this->stringToBoolConverting($data[$key], $value);
                 }
             } else {
                 if (isset($data[$value])) {
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
index 5e42c3aabccc036df1e61ba5ab63b61a8c04bf17..387c83f312b59266413902f37d1e6e156634b4c1 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
@@ -160,7 +160,7 @@ class Helper
 
         foreach ($useDefaults as $attributeCode => $useDefaultState) {
             if ($useDefaultState) {
-                $product->setData($attributeCode, false);
+                $product->setData($attributeCode, null);
             }
         }
 
diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php
index a47dd846a6d92805c6faf211d59e1921b49a4b02..e98e449d12bfada7624d94b5a2f8b2710f52ce2d 100644
--- a/app/code/Magento/Catalog/Model/Category.php
+++ b/app/code/Magento/Catalog/Model/Category.php
@@ -571,9 +571,9 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements
     public function getStoreId()
     {
         if ($this->hasData('store_id')) {
-            return $this->_getData('store_id');
+            return (int)$this->_getData('store_id');
         }
-        return $this->_storeManager->getStore()->getId();
+        return (int)$this->_storeManager->getStore()->getId();
     }
 
     /**
@@ -1261,6 +1261,7 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements
     }
 
     //@codeCoverageIgnoreStart
+
     /**
      * Set parent category ID
      *
diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php
index 924f96ded1adf4a480746a8661a3b72fc5f80a03..668803b3482bd10313f584be8a09930c4a585b60 100644
--- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php
+++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php
@@ -96,10 +96,10 @@ class Sortby extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
             if (!is_array($data)) {
                 $data = [];
             }
-            $object->setData($attributeCode, join(',', $data));
+            $object->setData($attributeCode, implode(',', $data) ?: null);
         }
         if (!$object->hasData($attributeCode)) {
-            $object->setData($attributeCode, false);
+            $object->setData($attributeCode, null);
         }
         return $this;
     }
diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php
index 5345072c4a5a679092c63a0d94ca958b9dc5b30b..6cebe3e1177a5be24294359a1008e1a8b4320f41 100644
--- a/app/code/Magento/Catalog/Model/Category/DataProvider.php
+++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php
@@ -11,9 +11,12 @@ use Magento\Eav\Api\Data\AttributeInterface;
 use Magento\Eav\Model\Config;
 use Magento\Eav\Model\Entity\Type;
 use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
+use Magento\Store\Model\Store;
 use Magento\Store\Model\StoreManagerInterface;
 use Magento\Ui\Component\Form\Field;
 use Magento\Ui\DataProvider\EavValidationRules;
+use Magento\Catalog\Model\CategoryFactory;
+use Magento\Framework\Exception\NoSuchEntityException;
 
 /**
  * Class DataProvider
@@ -22,6 +25,11 @@ use Magento\Ui\DataProvider\EavValidationRules;
  */
 class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
 {
+    /**
+     * @var string
+     */
+    protected $requestScopeFieldName = 'store';
+
     /**
      * @var array
      */
@@ -86,8 +94,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
     /**
      * @var \Magento\Framework\App\RequestInterface
      */
-    private $request;
-
+    protected $request;
 
     /**
      * @var Config
@@ -100,7 +107,12 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
     private $storeManager;
 
     /**
-     * Constructor
+     * @var CategoryFactory
+     */
+    private $categoryFactory;
+
+    /**
+     * DataProvider constructor
      *
      * @param string $name
      * @param string $primaryFieldName
@@ -111,9 +123,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
      * @param \Magento\Framework\Registry $registry
      * @param Config $eavConfig
      * @param \Magento\Framework\App\RequestInterface $request
+     * @param CategoryFactory $categoryFactory
      * @param array $meta
      * @param array $data
-     * @throws \Magento\Framework\Exception\LocalizedException
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -126,6 +138,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
         \Magento\Framework\Registry $registry,
         Config $eavConfig,
         \Magento\Framework\App\RequestInterface $request,
+        CategoryFactory $categoryFactory,
         array $meta = [],
         array $data = []
     ) {
@@ -136,6 +149,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
         $this->registry = $registry;
         $this->storeManager = $storeManager;
         $this->request = $request;
+        $this->categoryFactory = $categoryFactory;
         parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
         $this->meta = $this->prepareMeta($this->meta);
     }
@@ -282,7 +296,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
     protected function addUseDefaultSettings($category, $categoryData)
     {
         if ($category->getExistsStoreValueFlag('url_key') ||
-            $category->getStoreId() === \Magento\Store\Model\Store::DEFAULT_STORE_ID
+            $category->getStoreId() === Store::DEFAULT_STORE_ID
         ) {
             $categoryData['use_default']['url_key'] = false;
         } else {
@@ -296,10 +310,25 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
      * Get current category
      *
      * @return Category
+     * @throws NoSuchEntityException
      */
     public function getCurrentCategory()
     {
-        return $this->registry->registry('category');
+        $category = $this->registry->registry('category');
+        if ($category) {
+            return $category;
+        }
+        $requestId = $this->request->getParam($this->requestFieldName);
+        $requestScope = $this->request->getParam($this->requestScopeFieldName, Store::DEFAULT_STORE_ID);
+        if ($requestId) {
+            $category = $this->categoryFactory->create();
+            $category->setStoreId($requestScope);
+            $category->load($requestId);
+            if (!$category->getId()) {
+                throw NoSuchEntityException::singleField('id', $requestId);
+            }
+        }
+        return $category;
     }
 
     /**
@@ -349,7 +378,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
     public function getDefaultMetaData($result)
     {
         $result['parent']['default'] = (int)$this->request->getParam('parent');
-        $result['is_anchor']['default'] = false;
         $result['use_config.available_sort_by']['default'] = true;
         $result['use_config.default_sort_by']['default'] = true;
         $result['use_config.filter_price_range']['default'] = true;
@@ -418,6 +446,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
                 [
                     'custom_use_parent_settings',
                     'custom_apply_to_products',
+                    'custom_design',
                     'page_layout',
                     'custom_layout_update',
                 ],
@@ -425,7 +454,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
                 [
                     'custom_design_from',
                     'custom_design_to',
-                    'custom_design',
                 ],
             'category_view_optimization' =>
                 [
diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php
index d95c444d9f55cee3bad57614f080df0c9dd57362..ac693599985bce90b251eec0fcf74292976a62de 100644
--- a/app/code/Magento/Catalog/Model/CategoryRepository.php
+++ b/app/code/Magento/Catalog/Model/CategoryRepository.php
@@ -12,6 +12,9 @@ use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Framework\Exception\StateException;
 use Magento\Catalog\Api\Data\CategoryInterface;
 
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInterface
 {
     /**
@@ -50,18 +53,21 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
      * @param \Magento\Catalog\Model\CategoryFactory $categoryFactory
      * @param \Magento\Catalog\Model\ResourceModel\Category $categoryResource
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter
      * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool
      */
     public function __construct(
         \Magento\Catalog\Model\CategoryFactory $categoryFactory,
         \Magento\Catalog\Model\ResourceModel\Category $categoryResource,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
-        \Magento\Framework\Model\Entity\MetadataPool $metadataPool
+        \Magento\Framework\Model\Entity\MetadataPool $metadataPool,
+        \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter
     ) {
         $this->categoryFactory = $categoryFactory;
         $this->categoryResource = $categoryResource;
         $this->storeManager = $storeManager;
         $this->metadataPool = $metadataPool;
+        $this->extensibleDataObjectConverter = $extensibleDataObjectConverter;
     }
 
     /**
@@ -69,19 +75,19 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
      */
     public function save(\Magento\Catalog\Api\Data\CategoryInterface $category)
     {
-        $existingData = $category->toFlatArray();
-
-        /** 'available_sort_by' should be set separately because fields of array type are destroyed by toFlatArray() */
-        $existingData['available_sort_by'] = $category->getAvailableSortBy();
+        $storeId = (int)$this->storeManager->getStore()->getId();
+        $existingData = $this->extensibleDataObjectConverter
+            ->toNestedArray($category, [], 'Magento\Catalog\Api\Data\CategoryInterface');
+        $existingData = array_diff_key($existingData, array_flip(['path', 'level', 'parent_id']));
+        $existingData['store_id'] = $storeId;
 
         if ($category->getId()) {
             $metadata = $this->metadataPool->getMetadata(
                 CategoryInterface::class
             );
 
-            $existingCategory = $this->get($category->getId());
-
-            $existingData[$metadata->getLinkField()] = $existingCategory->getData(
+            $category = $this->get($category->getId(), $storeId);
+            $existingData[$metadata->getLinkField()] = $category->getData(
                 $metadata->getLinkField()
             );
 
@@ -89,22 +95,13 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
                 $existingData['image_additional_data'] = $existingData['image'];
                 unset($existingData['image']);
             }
-            $existingData['id'] = $existingCategory->getId();
-            $existingData['parent_id'] = $existingCategory->getParentId();
-            $existingData['path'] = $existingCategory->getPath();
-            $existingData['is_active'] = $existingCategory->getIsActive();
-            $existingData['include_in_menu'] =
-                isset($existingData['include_in_menu']) ? (bool)$existingData['include_in_menu'] : false;
-            $category->setData($existingData);
         } else {
             $parentId = $category->getParentId() ?: $this->storeManager->getStore()->getRootCategoryId();
-            $parentCategory = $this->get($parentId);
-            $existingData['include_in_menu'] =
-                isset($existingData['include_in_menu']) ? (bool)$existingData['include_in_menu'] : false;
-            /** @var  $category Category */
-            $category->setData($existingData);
-            $category->setPath($parentCategory->getPath());
+            $parentCategory = $this->get($parentId, $storeId);
+            $existingData['path'] = $parentCategory->getPath();
+            $existingData['parent_id'] = $parentId;
         }
+        $category->addData($existingData);
         try {
             $this->validateCategory($category);
             $this->categoryResource->save($category);
@@ -118,7 +115,7 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
             );
         }
         unset($this->instances[$category->getId()]);
-        return $category;
+        return $this->get($category->getId(), $storeId);
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php
index d59a209b84607d398ed1f3394e387fdab93ae41c..ebb3ce35c03775f551a2002eb0b3f40b783e890d 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php
@@ -5,11 +5,13 @@
  */
 namespace Magento\Catalog\Model\Indexer\Product\Flat;
 
+use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Model\Entity\MetadataPool;
 
 /**
  * Abstract action reindex class
- *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 abstract class AbstractAction
 {
@@ -72,6 +74,12 @@ abstract class AbstractAction
     protected $_flatTableBuilder;
 
     /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * @param MetadataPool $metadataPool
      * @param \Magento\Framework\App\ResourceConnection $resource
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper
@@ -80,6 +88,7 @@ abstract class AbstractAction
      * @param FlatTableBuilder $flatTableBuilder
      */
     public function __construct(
+        MetadataPool $metadataPool,
         \Magento\Framework\App\ResourceConnection $resource,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper,
@@ -93,6 +102,7 @@ abstract class AbstractAction
         $this->_connection = $resource->getConnection();
         $this->_tableBuilder = $tableBuilder;
         $this->_flatTableBuilder = $flatTableBuilder;
+        $this->metadataPool = $metadataPool;
     }
 
     /**
@@ -194,6 +204,8 @@ abstract class AbstractAction
             return $this;
         }
 
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
         foreach ($this->_getProductTypeInstances() as $typeInstance) {
             /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */
             if (!$typeInstance->isComposite(null)) {
@@ -210,7 +222,11 @@ abstract class AbstractAction
                 /** @var $select \Magento\Framework\DB\Select */
                 $select = $this->_connection->select()->from(
                     ['t' => $this->_productIndexerHelper->getTable($relation->getTable())],
-                    [$relation->getParentFieldName(), $relation->getChildFieldName(), new \Zend_Db_Expr('1')]
+                    [$relation->getChildFieldName(), new \Zend_Db_Expr('1')]
+                )->join(
+                    ['entity_table' => $this->_connection->getTableName('catalog_product_entity')],
+                    'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(),
+                    [$relation->getParentFieldName() => 'entity_table.entity_id']
                 )->join(
                     ['e' => $this->_productIndexerHelper->getFlatTableName($storeId)],
                     "e.entity_id = t.{$relation->getChildFieldName()}",
@@ -222,7 +238,7 @@ abstract class AbstractAction
                 if ($productIds !== null) {
                     $cond = [
                         $this->_connection->quoteInto("{$relation->getChildFieldName()} IN(?)", $productIds),
-                        $this->_connection->quoteInto("{$relation->getParentFieldName()} IN(?)", $productIds),
+                        $this->_connection->quoteInto("entity_table.entity_id IN(?)", $productIds),
                     ];
 
                     $select->where(implode(' OR ', $cond));
@@ -247,6 +263,8 @@ abstract class AbstractAction
             return $this;
         }
 
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
         foreach ($this->_getProductTypeInstances() as $typeInstance) {
             /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */
             if (!$typeInstance->isComposite(null)) {
@@ -258,11 +276,15 @@ abstract class AbstractAction
                 $select = $this->_connection->select()->distinct(
                     true
                 )->from(
-                    $this->_productIndexerHelper->getTable($relation->getTable()),
-                    "{$relation->getParentFieldName()}"
+                    ['t' => $this->_productIndexerHelper->getTable($relation->getTable())],
+                    []
+                )->join(
+                    ['entity_table' => $this->_connection->getTableName('catalog_product_entity')],
+                    'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(),
+                    [$relation->getParentFieldName() => 'entity_table.entity_id']
                 );
                 $joinLeftCond = [
-                    "e.entity_id = t.{$relation->getParentFieldName()}",
+                    "e.entity_id = entity_table.entity_id",
                     "e.child_id = t.{$relation->getChildFieldName()}",
                 ];
                 if ($relation->getWhere() !== null) {
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
index e0e784f8799de90c9eb7e1e18a50f7e9161f7841..6f4091b1cc7568b4918cba0a60c2f3e66ef3af4a 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
@@ -7,6 +7,7 @@ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action;
 
 use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder;
 use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder;
+use Magento\Framework\Model\Entity\MetadataPool;
 
 /**
  * Class Row reindex action
@@ -24,6 +25,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
     protected $flatItemEraser;
 
     /**
+     * @param MetadataPool $metadataPool
      * @param \Magento\Framework\App\ResourceConnection $resource
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper
@@ -34,6 +36,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
      * @param Eraser $flatItemEraser
      */
     public function __construct(
+        MetadataPool $metadataPool,
         \Magento\Framework\App\ResourceConnection $resource,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper,
@@ -44,6 +47,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
         Eraser $flatItemEraser
     ) {
         parent::__construct(
+            $metadataPool,
             $resource,
             $storeManager,
             $productHelper,
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php
index 36a5dbf013f571bf239a3fa50fe36e2fa4e34c3e..3153e2bfd54e62f8704c82d273e8de7f477ae7bd 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php
@@ -7,6 +7,7 @@ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action;
 
 use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder;
 use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder;
+use Magento\Framework\Model\Entity\MetadataPool;
 
 /**
  * Class Rows reindex action for mass actions
@@ -20,6 +21,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
     protected $flatItemEraser;
 
     /**
+     * @param MetadataPool $metadataPool
      * @param \Magento\Framework\App\ResourceConnection $resource
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper
@@ -29,6 +31,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
      * @param Eraser $flatItemEraser
      */
     public function __construct(
+        MetadataPool $metadataPool,
         \Magento\Framework\App\ResourceConnection $resource,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper,
@@ -38,6 +41,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
         Eraser $flatItemEraser
     ) {
         parent::__construct(
+            $metadataPool,
             $resource,
             $storeManager,
             $productHelper,
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php
index b42fc677f718e3fa112be20b85447e6b88795e32..3a91b567861d72e1549f6ea285a5c0354b03d765 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php
@@ -99,7 +99,7 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity
     protected function _isCallableAttributeInstance($instance, $method, $args)
     {
         if ($instance instanceof \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
-            && ($method == 'beforeSave' || $method = 'afterSave')
+            && ($method == 'beforeSave' || $method == 'afterSave')
         ) {
             $attributeCode = $instance->getAttribute()->getAttributeCode();
             if (isset($args[0]) && $args[0] instanceof \Magento\Framework\DataObject && $args[0]->getData($attributeCode) === false) {
@@ -467,19 +467,6 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity
         return $origObject;
     }
 
-    /**
-     * Check is attribute value empty
-     *
-     * @param AbstractAttribute $attribute
-     * @param mixed $value
-     * @return bool
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
-     */
-    protected function _isAttributeValueEmpty(AbstractAttribute $attribute, $value)
-    {
-        return $value === false;
-    }
-
     /**
      * Return if attribute exists in original data array.
      * Checks also attribute's store scope:
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index 57492bf78b705c1a7539bdba898eff168a6ad8a1..d3d72ae38273dfe55e2d3f3254713ef2965b79bb 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -73,6 +73,11 @@ class Category extends AbstractResource
      */
     protected $entityManager;
 
+    /**
+     * @var Category\AggregateCount
+     */
+    protected $aggregateCount;
+
     /**
      * Category constructor.
      * @param \Magento\Eav\Model\Entity\Context $context
@@ -82,6 +87,7 @@ class Category extends AbstractResource
      * @param Category\TreeFactory $categoryTreeFactory
      * @param Category\CollectionFactory $categoryCollectionFactory
      * @param EntityManager $entityManager
+     * @param Category\AggregateCount $aggregateCount
      * @param array $data
      */
     public function __construct(
@@ -92,6 +98,7 @@ class Category extends AbstractResource
         \Magento\Catalog\Model\ResourceModel\Category\TreeFactory $categoryTreeFactory,
         \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory,
         EntityManager $entityManager,
+        Category\AggregateCount $aggregateCount,
         $data = []
     ) {
         parent::__construct(
@@ -104,8 +111,8 @@ class Category extends AbstractResource
         $this->_categoryCollectionFactory = $categoryCollectionFactory;
         $this->_eventManager = $eventManager;
         $this->entityManager = $entityManager;
-
         $this->connectionName  = 'catalog';
+        $this->aggregateCount = $aggregateCount;
     }
 
     /**
@@ -184,20 +191,8 @@ class Category extends AbstractResource
     protected function _beforeDelete(\Magento\Framework\DataObject $object)
     {
         parent::_beforeDelete($object);
-
-        /**
-         * Update children count for all parent categories
-         */
-        $parentIds = $object->getParentIds();
-        if ($parentIds) {
-            $childDecrease = $object->getChildrenCount() + 1;
-            // +1 is itself
-            $data = ['children_count' => new \Zend_Db_Expr('children_count - ' . $childDecrease)];
-            $where = ['entity_id IN(?)' => $parentIds];
-            $this->getConnection()->update($this->getEntityTable(), $data, $where);
-        }
+        $this->aggregateCount->processDelete($object);
         $this->deleteChildren($object);
-        return $this;
     }
 
     /**
@@ -1009,7 +1004,6 @@ class Category extends AbstractResource
          */
         $this->entityManager->load(CategoryInterface::class, $object, $entityId);
 
-
         if (!$this->entityManager->has(\Magento\Catalog\Api\Data\CategoryInterface::class, $entityId)) {
             $object->isObjectNew(true);
         }
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php
new file mode 100644
index 0000000000000000000000000000000000000000..4599fc4a8428ac934c5989699b091f7094fbacc0
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Model\ResourceModel\Category;
+
+use Magento\Catalog\Model\Category;
+
+/**
+ * Class AggregateCount
+ */
+class AggregateCount
+{
+    /**
+     * @param Category $category
+     * @return void
+     */
+    public function processDelete(Category $category)
+    {
+        /** @var \Magento\Catalog\Model\ResourceModel\Category $resourceModel */
+        $resourceModel = $category->getResource();
+        /**
+         * Update children count for all parent categories
+         */
+        $parentIds = $category->getParentIds();
+        if ($parentIds) {
+            $childDecrease = $category->getChildrenCount() + 1;
+            // +1 is itself
+            $data = ['children_count' => new \Zend_Db_Expr('children_count - ' . $childDecrease)];
+            $where = ['entity_id IN(?)' => $parentIds];
+            $resourceModel->getConnection()->update($resourceModel->getEntityTable(), $data, $where);
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php
index 5a72330349dc5566726266b8aa7ec4420d70230b..ccc04da10abae97853971973d397b0b1d7642211 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php
@@ -66,6 +66,11 @@ class Product extends AbstractResource
      */
     protected $defaultAttributes;
 
+    /**
+     * @var array
+     */
+    protected $availableCategoryIdsCache = [];
+
     /**
      * @param \Magento\Eav\Model\Entity\Context $context
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -465,18 +470,22 @@ class Product extends AbstractResource
     {
         // is_parent=1 ensures that we'll get only category IDs those are direct parents of the product, instead of
         // fetching all parent IDs, including those are higher on the tree
-        $select = $this->getConnection()->select()->distinct()->from(
-            $this->getTable('catalog_category_product_index'),
-            ['category_id']
-        )->where(
-            'product_id = ? AND is_parent = 1',
-            (int)$object->getEntityId()
-        )->where(
-            'visibility != ?',
-            \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE
-        );
-
-        return $this->getConnection()->fetchCol($select);
+        $entityId = (int)$object->getEntityId();
+        if (!isset($this->availableCategoryIdsCache[$entityId])) {
+            $this->availableCategoryIdsCache[$entityId] = $this->getConnection()->fetchCol(
+                $this->getConnection()->select()->distinct()->from(
+                    $this->getTable('catalog_category_product_index'),
+                    ['category_id']
+                )->where(
+                    'product_id = ? AND is_parent = 1',
+                    $entityId
+                )->where(
+                    'visibility != ?',
+                    \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE
+                )
+            );
+        }
+        return $this->availableCategoryIdsCache[$entityId];
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php b/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b1ca4228abd4ed2fac5f2fc2636b6faf6691c81
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Plugin\Model\Attribute\Backend;
+
+class AttributeValidation
+{
+    /**
+     * @param \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend $subject
+     * @param \Closure $proceed
+     * @param \Magento\Framework\DataObject $attribute
+     * @return bool
+     */
+    public function aroundValidate(
+        \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend $subject,
+        \Closure $proceed,
+        \Magento\Framework\DataObject $attribute
+    ) {
+        $useDefault = $attribute->getUseDefault();
+        $attrCode = $subject->getAttribute()->getAttributeCode();
+        if ($useDefault && isset($useDefault[$attrCode])) {
+            return true;
+        }
+        return $proceed($attribute);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php
index fe26ac2a4481aedda0daf58ce240b1be20557401..33d441d3b8115c827671d7c80e1ef002ec7e1c69 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php
@@ -91,12 +91,12 @@ class SortbyTest extends \PHPUnit_Framework_TestCase
             'attribute does not exist' => [
                 self::DEFAULT_ATTRIBUTE_CODE,
                 [],
-                false,
+                null,
             ],
             'attribute sort by empty' => [
                 'available_sort_by',
                 ['available_sort_by' => null],
-                '',
+                null,
             ],
             'attribute sort by' => [
                 'available_sort_by',
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
index a774bab4793026208f40a28c067f4edd436ef50f..3e6f8ddf40b23381f5d89113bfa2e2ee28a9adbb 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
@@ -22,6 +22,16 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $categoryResourceMock;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $extensibleDataObjectConverterMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeMock;
+
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -44,6 +54,16 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->categoryResourceMock =
             $this->getMock('\Magento\Catalog\Model\ResourceModel\Category', [], [], '', false);
         $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+        $this->storeMock = $this->getMockBuilder('Magento\Store\Api\Data\StoreInterface')
+            ->disableOriginalConstructor()
+            ->setMethods(['getId'])
+            ->getMockForAbstractClass();
+        $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($this->storeMock);
+        $this->extensibleDataObjectConverterMock = $this
+            ->getMockBuilder('\Magento\Framework\Api\ExtensibleDataObjectConverter')
+            ->setMethods(['toNestedArray'])
+            ->disableOriginalConstructor()
+            ->getMock();
 
         $metadataMock = $this->getMock(
             'Magento\Framework\Model\Entity\EntityMetadata',
@@ -72,7 +92,8 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
             $this->categoryFactoryMock,
             $this->categoryResourceMock,
             $this->storeManagerMock,
-            $this->metadataPoolMock
+            $this->metadataPoolMock,
+            $this->extensibleDataObjectConverterMock
         );
     }
 
@@ -124,23 +145,58 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($categoryMock, $this->model->get($categoryId));
     }
 
-    public function testUpdateExistingCategory()
+    /**
+     * @return array
+     */
+    public function filterExtraFieldsOnUpdateCategoryDataProvider()
     {
-        $categoryId = 5;
+        return [
+            [
+                3,
+                ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category'],
+                [
+                    'store_id' => 1,
+                    'name' => 'category',
+                    'entity_id' => null
+                ]
+            ],
+            [
+                4,
+                ['level' => '1', 'path' => '1/2', 'image' => ['categoryImage'], 'name' => 'category'],
+                [
+                    'store_id' => 1,
+                    'image_additional_data' => ['categoryImage'],
+                    'name' => 'category',
+                    'entity_id' => null
+                ]
+            ]
+        ];
+    }
+
+    /**
+     * @param $categoryId
+     * @param $categoryData
+     * @param $dataForSave
+     * @dataProvider filterExtraFieldsOnUpdateCategoryDataProvider
+     */
+    public function testFilterExtraFieldsOnUpdateCategory($categoryId, $categoryData, $dataForSave)
+    {
+        $this->storeMock->expects($this->any())->method('getId')->willReturn(1);
         $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true);
         $categoryMock->expects(
             $this->atLeastOnce()
         )->method('getId')->willReturn($categoryId);
         $this->categoryFactoryMock->expects(
-            $this->once()
+            $this->exactly(2)
         )->method('create')->willReturn(
             $categoryMock
         );
-        $categoryMock->expects($this->atLeastOnce())->method('toFlatArray')->willReturn(['image' => []]);
+        $this->extensibleDataObjectConverterMock
+            ->expects($this->once())
+            ->method('toNestedArray')
+            ->will($this->returnValue($categoryData));
         $categoryMock->expects($this->once())->method('validate')->willReturn(true);
-        $categoryMock->expects($this->once())->method('getParentId')->willReturn(3);
-        $categoryMock->expects($this->once())->method('getPath')->willReturn('path');
-        $categoryMock->expects($this->once())->method('getIsActive')->willReturn(true);
+        $categoryMock->expects($this->once())->method('addData')->with($dataForSave);
         $this->categoryResourceMock->expects($this->once())
             ->method('save')
             ->willReturn('\Magento\Framework\DataObject');
@@ -149,21 +205,27 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
 
     public function testCreateNewCategory()
     {
+        $this->storeMock->expects($this->any())->method('getId')->willReturn(1);
         $categoryId = null;
         $parentCategoryId = 15;
         $newCategoryId = 25;
+        $categoryData = ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category'];
+        $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15,];
+        $this->extensibleDataObjectConverterMock
+            ->expects($this->once())
+            ->method('toNestedArray')
+            ->will($this->returnValue($categoryData));
         $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true);
         $parentCategoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true);
         $categoryMock->expects($this->any())->method('getId')
             ->will($this->onConsecutiveCalls($categoryId, $newCategoryId));
-        $categoryMock->expects($this->never())->method('getIsActive');
-        $this->categoryFactoryMock->expects($this->once())->method('create')->willReturn($parentCategoryMock);
+        $this->categoryFactoryMock->expects($this->exactly(2))->method('create')->willReturn($parentCategoryMock);
         $parentCategoryMock->expects($this->atLeastOnce())->method('getId')->willReturn($parentCategoryId);
 
         $categoryMock->expects($this->once())->method('getParentId')->willReturn($parentCategoryId);
         $parentCategoryMock->expects($this->once())->method('getPath')->willReturn('path');
+        $categoryMock->expects($this->once())->method('addData')->with($dataForSave);
         $categoryMock->expects($this->once())->method('validate')->willReturn(true);
-        $categoryMock->expects($this->once())->method('getParentId')->willReturn(3);
         $this->categoryResourceMock->expects($this->once())
             ->method('save')
             ->willReturn('\Magento\Framework\DataObject');
@@ -178,6 +240,10 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
     {
         $categoryId = 5;
         $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true);
+        $this->extensibleDataObjectConverterMock
+            ->expects($this->once())
+            ->method('toNestedArray')
+            ->will($this->returnValue([]));
         $categoryMock->expects(
             $this->atLeastOnce()
         )->method('getId')->willReturn($categoryId);
@@ -187,7 +253,6 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
             $categoryMock
         );
         $categoryMock->expects($this->once())->method('validate')->willReturn(false);
-        $categoryMock->expects($this->once())->method('getParentId')->willReturn(3);
         $this->model->save($categoryMock);
     }
 
@@ -199,6 +264,10 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->setExpectedException($expectedException, $expectedExceptionMessage);
         $categoryId = 5;
         $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false);
+        $this->extensibleDataObjectConverterMock
+            ->expects($this->once())
+            ->method('toNestedArray')
+            ->will($this->returnValue([]));
         $objectMock = $this->getMock('\Magento\Framework\DataObject', ['getFrontend', 'getLabel'], [], '', false);
         $categoryMock->expects(
             $this->atLeastOnce()
@@ -210,7 +279,6 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase
         );
         $objectMock->expects($this->any())->method('getFrontend')->willReturn($objectMock);
         $objectMock->expects($this->any())->method('getLabel')->willReturn('ValidateCategoryTest');
-        $categoryMock->expects($this->once())->method('getParentId')->willReturn(3);
         $categoryMock->expects($this->once())->method('validate')->willReturn([42 => $error]);
         $this->categoryResourceMock->expects($this->any())->method('getAttribute')->with(42)->willReturn($objectMock);
         $categoryMock->expects($this->never())->method('unsetData');
diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
index ffeed2a2d25f15ce6907d4816771fa3236c1d4ca..d25285265394847ca85fe04f58b02737a28ab166 100644
--- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
+++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
@@ -43,6 +43,7 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column
 
     /**
      * {@inheritdoc}
+     * @deprecated
      */
     public function prepareDataSource(array $dataSource)
     {
@@ -63,15 +64,16 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column
 
         return $dataSource;
     }
-
+    
     /**
      * Prepare component configuration
      * @return void
      */
     public function prepare()
     {
-        if (!$this->storeManager->isSingleStoreMode()) {
-            parent::prepare();
+        parent::prepare();
+        if ($this->storeManager->isSingleStoreMode()) {
+            $this->_data['config']['componentDisabled'] = true;
         }
     }
 }
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 2227f27b94a3ea3f85dadf88e9da345a686501ef..0cb04569e0ed455a94b2b759d44b9bb1c6305bae 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -477,6 +477,9 @@
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
         <plugin name="transactionWrapper" type="Magento\Catalog\Model\Plugin\ProductRepository\TransactionWrapper" sortOrder="-1"/>
     </type>
+    <type name="Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend">
+        <plugin name="attributeValidation" type="Magento\Catalog\Plugin\Model\Attribute\Backend\AttributeValidation"/>
+    </type>
     <type name="Magento\Catalog\Model\CategoryRepository">
         <arguments>
             <argument name="categoryResource" xsi:type="object">Magento\Catalog\Model\ResourceModel\Category\Proxy</argument>
diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml
index f0c892107d4659f4745cb8bab972e2171bf807f6..0bbd48f56e0620f25ca6b152ba165790c123c882 100644
--- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml
@@ -252,9 +252,10 @@
                     <item name="label" xsi:type="string" translate="true">Anchor</item>
                     <item name="prefer" xsi:type="string">toggle</item>
                     <item name="valueMap" xsi:type="array">
-                        <item name="true" xsi:type="boolean">true</item>
-                        <item name="false" xsi:type="boolean">false</item>
+                        <item name="true" xsi:type="string">1</item>
+                        <item name="false" xsi:type="string">0</item>
                     </item>
+                    <item name="default" xsi:type="number">1</item>
                 </item>
             </argument>
         </field>
@@ -487,12 +488,25 @@
                 </item>
             </argument>
         </field>
-        <field name="page_layout">
+        <field name="custom_design">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
                     <item name="sortOrder" xsi:type="number">180</item>
                     <item name="dataType" xsi:type="string">string</item>
                     <item name="formElement" xsi:type="string">select</item>
+                    <item name="label" xsi:type="string" translate="true">Theme</item>
+                    <item name="imports" xsi:type="array">
+                        <item name="disabled" xsi:type="string">${ $.parentName }.custom_use_parent_settings:checked</item>
+                    </item>
+                </item>
+            </argument>
+        </field>
+        <field name="page_layout">
+            <argument name="data" xsi:type="array">
+                <item name="config" xsi:type="array">
+                    <item name="sortOrder" xsi:type="number">190</item>
+                    <item name="dataType" xsi:type="string">string</item>
+                    <item name="formElement" xsi:type="string">select</item>
                     <item name="label" xsi:type="string" translate="true">Layout</item>
                     <item name="imports" xsi:type="array">
                         <item name="disabled" xsi:type="string">${ $.parentName }.custom_use_parent_settings:checked</item>
@@ -503,7 +517,7 @@
         <field name="custom_layout_update">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
-                    <item name="sortOrder" xsi:type="number">190</item>
+                    <item name="sortOrder" xsi:type="number">200</item>
                     <item name="dataType" xsi:type="string">string</item>
                     <item name="formElement" xsi:type="string">textarea</item>
                     <item name="label" xsi:type="string" translate="true">Layout Update XML</item>
@@ -517,7 +531,7 @@
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
                     <item name="additionalClasses" xsi:type="string">admin__field-no-label</item>
-                    <item name="sortOrder" xsi:type="number">200</item>
+                    <item name="sortOrder" xsi:type="number">210</item>
                     <item name="label" xsi:type="string"/>
                     <item name="description" xsi:type="string" translate="true">Apply Design to Products</item>
                     <item name="dataType" xsi:type="string">boolean</item>
@@ -549,7 +563,8 @@
                     <item name="additionalClasses" xsi:type="string">admin__control-grouped-date</item>
                     <item name="component" xsi:type="string">Magento_Ui/js/form/components/group</item>
                     <item name="label" xsi:type="string" translate="true">Schedule Update From</item>
-                    <item name="sortOrder" xsi:type="number">210</item>
+                    <item name="required" xsi:type="boolean">false</item>
+                    <item name="sortOrder" xsi:type="number">220</item>
                     <item name="breakLine" xsi:type="boolean">false</item>
                     <item name="imports" xsi:type="array">
                         <item name="disabled" xsi:type="string">ns = ${ $.ns }, index = custom_use_parent_settings :checked</item>
@@ -560,7 +575,7 @@
                 <argument name="data" xsi:type="array">
                     <item name="config" xsi:type="array">
                         <item name="additionalClasses" xsi:type="string">admin__field-date</item>
-                        <item name="sortOrder" xsi:type="number">220</item>
+                        <item name="sortOrder" xsi:type="number">230</item>
                         <item name="dataType" xsi:type="string">string</item>
                         <item name="formElement" xsi:type="string">date</item>
                         <item name="imports" xsi:type="array">
@@ -573,7 +588,7 @@
                 <argument name="data" xsi:type="array">
                     <item name="config" xsi:type="array">
                         <item name="additionalClasses" xsi:type="string">admin__field-date</item>
-                        <item name="sortOrder" xsi:type="number">230</item>
+                        <item name="sortOrder" xsi:type="number">240</item>
                         <item name="dataType" xsi:type="string">string</item>
                         <item name="label" xsi:type="string" translate="true">To</item>
                         <item name="formElement" xsi:type="string">date</item>
@@ -584,18 +599,5 @@
                 </argument>
             </field>
         </container>
-        <field name="custom_design">
-            <argument name="data" xsi:type="array">
-                <item name="config" xsi:type="array">
-                    <item name="sortOrder" xsi:type="number">240</item>
-                    <item name="dataType" xsi:type="string">string</item>
-                    <item name="formElement" xsi:type="string">select</item>
-                    <item name="label" xsi:type="string" translate="true">New Theme</item>
-                    <item name="imports" xsi:type="array">
-                        <item name="disabled" xsi:type="string">ns = ${ $.ns }, index = custom_use_parent_settings :checked</item>
-                    </item>
-                </item>
-            </argument>
-        </field>
     </fieldset>
 </form>
diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
index 346b96024c8b245f2e6ae1cb6bb3404e4008044e..8f6792b862d2ce93c9e14e0086977ff83a3dd16f 100644
--- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
@@ -230,7 +230,7 @@
                 </item>
             </argument>
         </column>
-        <column name="websites">
+        <column name="websites" class="Magento\Catalog\Ui\Component\Listing\Columns\Websites">
             <argument name="data" xsi:type="array">
                 <item name="options" xsi:type="object">Magento\Store\Model\ResourceModel\Website\Collection</item>
                 <item name="config" xsi:type="array">
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index f13343a80e1c239f2e7a809c230d8078f416702d..6ce20ca28a736080c6442f5eb938a6ad080108fc 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -2163,7 +2163,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
 
         if (!$this->validator->isValid($rowData)) {
             foreach ($this->validator->getMessages() as $message) {
-                $this->addRowError($message, $rowNum);
+                $this->addRowError($message, $rowNum, $this->validator->getInvalidAttribute());
             }
         }
 
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
index 961e5c2d3a1cacc7ecd52f852a2ec89b25d45c51..17164013b87b75409010b1e6f7067c604e367a64 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
@@ -35,6 +35,11 @@ class Validator extends AbstractValidator implements RowValidatorInterface
      */
     protected $_rowData;
 
+    /*
+     * @var string|null
+     */
+    protected $invalidAttribute;
+
     /**
      * @param \Magento\Framework\Stdlib\StringUtils $string
      * @param RowValidatorInterface[] $validators
@@ -200,10 +205,32 @@ class Validator extends AbstractValidator implements RowValidatorInterface
             }
             $this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[Product::COL_SKU];
         }
+        
+        if (!$valid) {
+            $this->setInvalidAttribute($attrCode);
+        }
+
         return (bool)$valid;
 
     }
 
+    /**
+     * @param string|null $attribute
+     * @return void
+     */
+    protected function setInvalidAttribute($attribute)
+    {
+        $this->invalidAttribute = $attribute;
+    }
+
+    /**
+     * @return string
+     */
+    public function getInvalidAttribute()
+    {
+        return $this->invalidAttribute;
+    }
+
     /**
      * @return bool
      * @SuppressWarnings(PHPMD.UnusedLocalVariable)
@@ -211,6 +238,7 @@ class Validator extends AbstractValidator implements RowValidatorInterface
     protected function isValidAttributes()
     {
         $this->_clearMessages();
+        $this->setInvalidAttribute(null);
         if (!isset($this->_rowData['product_type'])) {
             return false;
         }
diff --git a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php
index 3953bb9d6b79eb1432cb8fcda76f2f03059d41d1..50b60a5434aaf0bc3874aca6ba555ec86e589e91 100644
--- a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php
+++ b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php
@@ -153,7 +153,7 @@ class Conditions extends Generic implements TabInterface
 
         $renderer = $this->_rendererFieldset->setTemplate('Magento_CatalogRule::promo/fieldset.phtml')
             ->setNewChildUrl($newChildUrl)
-            ->setConditionsFieldSetId($model->getConditionsFieldSetId($formName));
+            ->setFieldSetId($model->getConditionsFieldSetId($formName));
 
         $fieldset = $form->addFieldset(
             $fieldsetId,
diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php
index bb94d52397bbe73fdc8bd6b62d1cb8bbffa6cf3e..aadacc9074f8e22765c21ae1d7dd32cd4d0bdbdc 100644
--- a/app/code/Magento/CatalogRule/Model/Rule.php
+++ b/app/code/Magento/CatalogRule/Model/Rule.php
@@ -337,7 +337,7 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog
     protected function _getWebsitesMap()
     {
         $map = [];
-        $websites = $this->_storeManager->getWebsites(true);
+        $websites = $this->_storeManager->getWebsites();
         foreach ($websites as $website) {
             // Continue if website has no store to be able to create catalog rule for website without store
             if ($website->getDefaultStore() === null) {
@@ -582,6 +582,7 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog
     }
 
     //@codeCoverageIgnoreStart
+
     /**
      * {@inheritdoc}
      */
diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php
index bb547ca2859adf4e3438e0528cdd520b51d1266c..66bf230a43391b12d36859f020b2b9d9edd7d5f7 100644
--- a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php
+++ b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php
@@ -163,7 +163,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase
             'created_at' => '2014-06-25 13:14:30',
             'updated_at' => '2014-06-25 14:37:15'
         ];
-        $this->storeManager->expects($this->any())->method('getWebsites')->with(true)
+        $this->storeManager->expects($this->any())->method('getWebsites')->with(false)
             ->will($this->returnValue([$this->websiteModel, $this->websiteModel]));
         $this->websiteModel->expects($this->at(0))->method('getId')
             ->will($this->returnValue('1'));
diff --git a/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml b/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml
index 714f80fd47e8acef6476318fa150a1228bd8bebb..3cd22ae688f37ad1e5f39e683688240ca5f75b9f 100644
--- a/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml
+++ b/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml
@@ -9,7 +9,7 @@
 /**@var \Magento\Backend\Block\Widget\Form\Renderer\Fieldset $block */
 ?>
 <?php $_element = $block->getElement() ?>
-<?php $_jsObjectName = $block->getConditionsFieldSetId() != null ? $block->getConditionsFieldSetId() : $_element->getHtmlId() ?>
+<?php $_jsObjectName = $block->getFieldSetId() != null ? $block->getFieldSetId() : $_element->getHtmlId() ?>
 <div class="rule-tree">
     <fieldset id="<?php /* @escapeNotVerified */ echo $_jsObjectName ?>" <?php /* @escapeNotVerified */ echo $_element->serialize(['class']) ?> class="fieldset">
         <legend class="legend"><span><?php /* @escapeNotVerified */ echo $_element->getLegend() ?></span></legend>
diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php
index 7899c1bcbd33117479af255361e5ebfa1a9c4fc3..648a849a81a4edfa1fca5cb4d4bbdb551b83b3b1 100644
--- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php
+++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php
@@ -11,6 +11,11 @@ class ConfigurableProductsProvider
     /** @var \Magento\Framework\App\ResourceConnection */
     private $resource;
 
+    /**
+     * @var array
+     */
+    private $productIds = [];
+
     /**
      * @param \Magento\Framework\App\ResourceConnection $resource
      */
@@ -25,13 +30,17 @@ class ConfigurableProductsProvider
      */
     public function getIds(array $ids)
     {
-        $connection = $this->resource->getConnection();
-        return $connection->fetchCol(
-            $connection
-                ->select()
-                ->from(['e' => $this->resource->getTableName('catalog_product_entity')], ['e.entity_id'])
-                ->where('e.type_id = ?', \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE)
-                ->where('e.entity_id IN (?)', $ids)
-        );
+        $key =  md5(json_encode($ids));
+        if (!isset($this->productIds[$key])) {
+            $connection = $this->resource->getConnection();
+            $this->productIds[$key] = $connection->fetchCol(
+                $connection
+                    ->select()
+                    ->from(['e' => $this->resource->getTableName('catalog_product_entity')], ['e.entity_id'])
+                    ->where('e.type_id = ?', \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE)
+                    ->where('e.entity_id IN (?)', $ids)
+            );
+        }
+        return $this->productIds[$key];
     }
 }
diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php
index 8c7eff09a889c70f5b50c21b2eb0154ff0e2aa15..855acac70751d6a406e894baf71dea1271bf7791 100644
--- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php
+++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php
@@ -20,6 +20,11 @@ class ConfigurableProductHandler
     /** @var ConfigurableProductsProvider */
     private $configurableProductsProvider;
 
+    /**
+     * @var array
+     */
+    private $childrenProducts = [];
+
     /**
      * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurable
      * @param ConfigurableProductsProvider $configurableProductsProvider
@@ -42,7 +47,10 @@ class ConfigurableProductHandler
     {
         $configurableProductIds = $this->configurableProductsProvider->getIds(array_keys($productIds));
         foreach ($configurableProductIds as $productId) {
-            $subProductIds = $this->configurable->getChildrenIds($productId)[0];
+            if (!isset($this->childrenProducts[$productId])) {
+                $this->childrenProducts[$productId] = $this->configurable->getChildrenIds($productId)[0];
+            }
+            $subProductIds = $this->childrenProducts[$productId];
             $parentValidationResult = isset($productIds[$productId])
                 ? array_filter($productIds[$productId])
                 : [];
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php
index 577e0aa871420f3fa5a5135a10ca05632b128939..7d6370a2ca341a20241eb713ed35f0067f088e3a 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php
@@ -372,21 +372,13 @@ class DataProvider
                 ['main' => $this->getTable($relation->getTable())],
                 [$relation->getChildFieldName()]
             );
-            //TODO: Will be removed in MAGETWO-47395
-            if ($typeId === 'configurable') {
-                $select->where(
-                    $relation->getParentFieldName() . ' = ?',
-                    $productId
-                );
-            } else {
-                $select->join(
-                    ['e' => $this->resource->getTableName('catalog_product_entity')],
-                    'e.' . $this->metadata->getLinkField() . ' = main.' . $relation->getParentFieldName()
-                )->where(
-                    'e.entity_id = ?',
-                    $productId
-                );
-            }
+            $select->join(
+                ['e' => $this->resource->getTableName('catalog_product_entity')],
+                'e.' . $this->metadata->getLinkField() . ' = main.' . $relation->getParentFieldName()
+            )->where(
+                'e.entity_id = ?',
+                $productId
+            );
 
             if ($relation->getWhere() !== null) {
                 $select->where($relation->getWhere());
diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php
index b57768ea9d283e8dcec2572c46d9baa998114d1d..6b54c8cbc3118976e31eae672d0bc81f8c85e6dd 100644
--- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php
+++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php
@@ -6,6 +6,8 @@
 namespace Magento\CatalogSearch\Model\ResourceModel\Advanced;
 
 use Magento\Catalog\Model\Product;
+use Magento\Framework\Api\FilterBuilder;
+use Magento\Framework\Api\Search\SearchCriteriaBuilder;
 use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage;
 
 /**
@@ -23,19 +25,24 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
     private $filters = [];
 
     /**
-     * @var \Magento\CatalogSearch\Model\Advanced\Request\Builder
+     * @var \Magento\Search\Api\SearchInterface
      */
-    private $requestBuilder;
+    private $search;
 
     /**
-     * @var \Magento\Search\Model\SearchEngine
+     * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory
      */
-    private $searchEngine;
+    private $temporaryStorageFactory;
 
     /**
-     * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory
+     * @var SearchCriteriaBuilder
      */
-    private $temporaryStorageFactory;
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var FilterBuilder
+     */
+    private $filterBuilder;
 
     /**
      * Collection constructor.
@@ -59,10 +66,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      * @param \Magento\Framework\Stdlib\DateTime $dateTime
      * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement
      * @param \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation
-     * @param \Magento\CatalogSearch\Model\Advanced\Request\Builder $requestBuilder
-     * @param \Magento\Search\Model\SearchEngine $searchEngine
+     * @param \Magento\Search\Api\SearchInterface $search
      * @param \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory
-     * @param \Zend_Db_Adapter_Abstract $connection
+     * @param SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param FilterBuilder $filterBuilder
+     * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -86,14 +94,16 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
         \Magento\Framework\Stdlib\DateTime $dateTime,
         \Magento\Customer\Api\GroupManagementInterface $groupManagement,
         \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation,
-        \Magento\CatalogSearch\Model\Advanced\Request\Builder $requestBuilder,
-        \Magento\Search\Model\SearchEngine $searchEngine,
+        \Magento\Search\Api\SearchInterface $search,
         \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory,
-        $connection = null
+        SearchCriteriaBuilder $searchCriteriaBuilder,
+        FilterBuilder $filterBuilder,
+        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null
     ) {
-        $this->requestBuilder = $requestBuilder;
-        $this->searchEngine = $searchEngine;
+        $this->search = $search;
         $this->temporaryStorageFactory = $temporaryStorageFactory;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->filterBuilder = $filterBuilder;
         parent::__construct(
             $entityFactory,
             $logger,
@@ -140,19 +150,18 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
     protected function _renderFiltersBefore()
     {
         if ($this->filters) {
-            $this->requestBuilder->bindDimension('scope', $this->getStoreId());
-            $this->requestBuilder->setRequestName('advanced_search_container');
             foreach ($this->filters as $attributes) {
                 foreach ($attributes as $attributeCode => $attributeValue) {
                     $attributeCode = $this->getAttributeCode($attributeCode);
-                    $this->requestBuilder->bindRequestValue($attributeCode, $attributeValue);
+                    $this->addAttributeToSearch($attributeCode, $attributeValue);
                 }
             }
-            $queryRequest = $this->requestBuilder->create();
-            $queryResponse = $this->searchEngine->search($queryRequest);
+            $searchCriteria = $this->searchCriteriaBuilder->create();
+            $searchCriteria->setRequestName('advanced_search_container');
+            $searchResult = $this->search->search($searchCriteria);
 
             $temporaryStorage = $this->temporaryStorageFactory->create();
-            $table = $temporaryStorage->storeDocuments($queryResponse->getIterator());
+            $table = $temporaryStorage->storeApiDocuments($searchResult->getItems());
 
             $this->getSelect()->joinInner(
                 [
@@ -162,7 +171,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
                 []
             );
         }
-        return parent::_renderFiltersBefore();
+        parent::_renderFiltersBefore();
     }
 
     /**
@@ -178,4 +187,49 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
 
         return $attributeCode;
     }
+
+    /**
+     * Create a filter and add it to the SearchCriteriaBuilder.
+     *
+     * @param string $attributeCode
+     * @param array|string $attributeValue
+     * @return void
+     */
+    private function addAttributeToSearch($attributeCode, $attributeValue)
+    {
+        if (isset($attributeValue['from']) || isset($attributeValue['to'])) {
+            $this->addRangeAttributeToSearch($attributeCode, $attributeValue);
+        } elseif (!is_array($attributeValue)) {
+            $this->filterBuilder->setField($attributeCode)->setValue($attributeValue);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        } elseif (isset($attributeValue['like'])) {
+            $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['like']);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        } elseif (isset($attributeValue['in'])) {
+            $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['in']);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        } elseif (isset($attributeValue['in_set'])) {
+            $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['in_set']);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        }
+    }
+
+    /**
+     * Add attributes that have a range (from,to) to the SearchCriteriaBuilder.
+     *
+     * @param string $attributeCode
+     * @param array|string $attributeValue
+     * @return void
+     */
+    private function addRangeAttributeToSearch($attributeCode, $attributeValue)
+    {
+        if (isset($attributeValue['from']) && '' !== $attributeValue['from']) {
+            $this->filterBuilder->setField("{$attributeCode}.from")->setValue($attributeValue['from']);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        }
+        if (isset($attributeValue['to']) && '' !== $attributeValue['to']) {
+            $this->filterBuilder->setField("{$attributeCode}.to")->setValue($attributeValue['to']);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
+        }
+    }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php
index 9b2eb2876b403e1b7cd8ec2837af8cf3a93a6912..740786c7cbe3dc541206e76b4dc680e36f2d403c 100644
--- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php
+++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php
@@ -9,6 +9,7 @@ use Magento\Framework\DB\Select;
 use Magento\Framework\Exception\StateException;
 use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage;
 use Magento\Framework\Search\Response\QueryResponse;
+use Magento\Framework\App\ObjectManager;
 
 /**
  * Fulltext Collection
@@ -16,23 +17,29 @@ use Magento\Framework\Search\Response\QueryResponse;
  */
 class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
 {
-    /** @var  QueryResponse */
+    /**
+     * @var  QueryResponse
+     * @deprecated
+     */
     protected $queryResponse;
 
     /**
      * Catalog search data
      *
      * @var \Magento\Search\Model\QueryFactory
+     * @deprecated
      */
     protected $queryFactory = null;
 
     /**
      * @var \Magento\Framework\Search\Request\Builder
+     * @deprecated
      */
     private $requestBuilder;
 
     /**
      * @var \Magento\Search\Model\SearchEngine
+     * @deprecated
      */
     private $searchEngine;
 
@@ -56,6 +63,26 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      */
     private $temporaryStorageFactory;
 
+    /**
+     * @var \Magento\Search\Api\SearchInterface
+     */
+    private $search;
+
+    /**
+     * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var \Magento\Framework\Api\Search\SearchResultInterface
+     */
+    private $searchResult;
+
+    /**
+     * @var \Magento\Framework\Api\FilterBuilder
+     */
+    private $filterBuilder;
+
     /**
      * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
      * @param \Psr\Log\LoggerInterface $logger
@@ -143,6 +170,73 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
         $this->searchRequestName = $searchRequestName;
     }
 
+    /**
+     * @deprecated
+     * @return \Magento\Search\Api\SearchInterface
+     */
+    private function getSearch()
+    {
+        if ($this->search === null) {
+            $this->search = ObjectManager::getInstance()->get('\Magento\Search\Api\SearchInterface');
+        }
+        return $this->search;
+    }
+
+    /**
+     * @deprecated
+     * @param \Magento\Search\Api\SearchInterface $object
+     * @return void
+     */
+    public function setSearch(\Magento\Search\Api\SearchInterface $object)
+    {
+        $this->search = $object;
+    }
+
+    /**
+     * @deprecated
+     * @return \Magento\Framework\Api\Search\SearchCriteriaBuilder
+     */
+    private function getSearchCriteriaBuilder()
+    {
+        if ($this->searchCriteriaBuilder === null) {
+            $this->searchCriteriaBuilder = ObjectManager::getInstance()
+                ->get('\Magento\Framework\Api\Search\SearchCriteriaBuilder');
+        }
+        return $this->searchCriteriaBuilder;
+    }
+
+    /**
+     * @deprecated
+     * @param \Magento\Framework\Api\Search\SearchCriteriaBuilder $object
+     * @return void
+     */
+    public function setSearchCriteriaBuilder(\Magento\Framework\Api\Search\SearchCriteriaBuilder $object)
+    {
+        $this->searchCriteriaBuilder = $object;
+    }
+
+    /**
+     * @deprecated
+     * @return \Magento\Framework\Api\FilterBuilder
+     */
+    private function getFilterBuilder()
+    {
+        if ($this->filterBuilder === null) {
+            $this->filterBuilder = ObjectManager::getInstance()->get('\Magento\Framework\Api\FilterBuilder');
+        }
+        return $this->filterBuilder;
+    }
+
+    /**
+     * @deprecated
+     * @param \Magento\Framework\Api\FilterBuilder $object
+     * @return void
+     */
+    public function setFilterBuilder(\Magento\Framework\Api\FilterBuilder $object)
+    {
+        $this->filterBuilder = $object;
+    }
+
     /**
      * Apply attribute filter to facet collection
      *
@@ -152,17 +246,26 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      */
     public function addFieldToFilter($field, $condition = null)
     {
-        if ($this->queryResponse !== null) {
+        if ($this->searchResult !== null) {
             throw new \RuntimeException('Illegal state');
         }
+
+        $this->getSearchCriteriaBuilder();
+        $this->getFilterBuilder();
         if (!is_array($condition) || !in_array(key($condition), ['from', 'to'])) {
-            $this->requestBuilder->bind($field, $condition);
+            $this->filterBuilder->setField($field);
+            $this->filterBuilder->setValue($condition);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
         } else {
             if (!empty($condition['from'])) {
-                $this->requestBuilder->bind("{$field}.from", $condition['from']);
+                $this->filterBuilder->setField("{$field}.from");
+                $this->filterBuilder->setValue($condition['from']);
+                $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
             }
             if (!empty($condition['to'])) {
-                $this->requestBuilder->bind("{$field}.to", $condition['to']);
+                $this->filterBuilder->setField("{$field}.to");
+                $this->filterBuilder->setValue($condition['to']);
+                $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
             }
         }
         return $this;
@@ -185,9 +288,14 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      */
     protected function _renderFiltersBefore()
     {
-        $this->requestBuilder->bindDimension('scope', $this->getStoreId());
+        $this->getSearchCriteriaBuilder();
+        $this->getFilterBuilder();
+        $this->getSearch();
+
         if ($this->queryText) {
-            $this->requestBuilder->bind('search_term', $this->queryText);
+            $this->filterBuilder->setField('search_term');
+            $this->filterBuilder->setValue($this->queryText);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
         }
 
         $priceRangeCalculation = $this->_scopeConfig->getValue(
@@ -195,16 +303,17 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
             \Magento\Store\Model\ScopeInterface::SCOPE_STORE
         );
         if ($priceRangeCalculation) {
-            $this->requestBuilder->bind('price_dynamic_algorithm', $priceRangeCalculation);
+            $this->filterBuilder->setField('price_dynamic_algorithm');
+            $this->filterBuilder->setValue($priceRangeCalculation);
+            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
         }
 
-        $this->requestBuilder->setRequestName($this->searchRequestName);
-        $queryRequest = $this->requestBuilder->create();
-
-        $this->queryResponse = $this->searchEngine->search($queryRequest);
+        $searchCriteria = $this->searchCriteriaBuilder->create();
+        $searchCriteria->setRequestName($this->searchRequestName);
+        $this->searchResult = $this->search->search($searchCriteria);
 
         $temporaryStorage = $this->temporaryStorageFactory->create();
-        $table = $temporaryStorage->storeDocuments($this->queryResponse->getIterator());
+        $table = $temporaryStorage->storeApiDocuments($this->searchResult->getItems());
 
         $this->getSelect()->joinInner(
             [
@@ -214,7 +323,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
             []
         );
 
-        $this->_totalRecords = $this->queryResponse->count();
+        $this->_totalRecords = $this->searchResult->getTotalCount();
 
         if ($this->order && 'relevance' === $this->order['field']) {
             $this->getSelect()->order('search_result.'. TemporaryStorage::FIELD_SCORE . ' ' . $this->order['dir']);
@@ -268,7 +377,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
     {
         $this->_renderFilters();
         $result = [];
-        $aggregations = $this->queryResponse->getAggregations();
+        $aggregations = $this->searchResult->getAggregations();
         $bucket = $aggregations->getBucket($field . '_bucket');
         if ($bucket) {
             foreach ($bucket->getValues() as $value) {
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..353533b9c2e981ef717c3a5a7c6dfbb371c55b02
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel\Advanced;
+
+use Magento\Catalog\Model\Product;
+use Magento\CatalogSearch\Test\Unit\Model\ResourceModel\BaseCollectionTest;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+/**
+ * Tests Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection
+ */
+class CollectionTest extends BaseCollectionTest
+{
+    /**
+     * @var \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection
+     */
+    private $advancedCollection;
+
+    /**
+     * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filterBuilder;
+
+    /**
+     * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $criteriaBuilder;
+
+    /**
+     * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $temporaryStorageFactory;
+
+    /**
+     * @var \Magento\Search\Api\SearchInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $search;
+
+    /**
+     * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $eavConfig;
+
+    /**
+     * setUp method for CollectionTest
+     */
+    protected function setUp()
+    {
+        $helper = new ObjectManagerHelper($this);
+
+        $this->eavConfig = $this->getMock('Magento\Eav\Model\Config', [], [], '', false);
+        $storeManager = $this->getStoreManager();
+        $universalFactory = $this->getUniversalFactory();
+        $this->criteriaBuilder = $this->getCriteriaBuilder();
+        $this->filterBuilder = $this->getMock('Magento\Framework\Api\FilterBuilder', [], [], '', false);
+        $this->temporaryStorageFactory = $this->getMock(
+            'Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->search = $this->getMock('Magento\Search\Api\SearchInterface', [], [], '', false);
+
+        $this->advancedCollection = $helper->getObject(
+            'Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection',
+            [
+                'eavConfig' => $this->eavConfig,
+                'storeManager' => $storeManager,
+                'universalFactory' => $universalFactory,
+                'searchCriteriaBuilder' => $this->criteriaBuilder,
+                'filterBuilder' => $this->filterBuilder,
+                'temporaryStorageFactory' => $this->temporaryStorageFactory,
+                'search' => $this->search,
+            ]
+        );
+    }
+
+    public function testLoadWithFilterNoFilters()
+    {
+        $this->advancedCollection->loadWithFilter();
+    }
+
+    /**
+     * Test a search using 'like' condition
+     */
+    public function testLike()
+    {
+        $attributeCode = 'description';
+        $attributeCodeId = 42;
+        $attribute = $this->getMock('Magento\Catalog\Model\ResourceModel\Eav\Attribute', [], [], '', false);
+        $attribute->expects($this->once())->method('getAttributeCode')->willReturn($attributeCode);
+        $this->eavConfig->expects($this->once())->method('getAttribute')->with(Product::ENTITY, $attributeCodeId)
+            ->willReturn($attribute);
+        $filtersData = ['catalog_product_entity_text' => [$attributeCodeId => ['like' => 'search text']]];
+        $this->filterBuilder->expects($this->once())->method('setField')->with($attributeCode)
+            ->willReturn($this->filterBuilder);
+        $this->filterBuilder->expects($this->once())->method('setValue')->with('search text')
+            ->willReturn($this->filterBuilder);
+
+        $filter = $this->getMock('Magento\Framework\Api\Filter');
+        $this->filterBuilder->expects($this->once())->method('create')->willReturn($filter);
+
+        $criteria = $this->getMock('Magento\Framework\Api\Search\SearchCriteria', [], [], '', false);
+        $this->criteriaBuilder->expects($this->once())->method('create')->willReturn($criteria);
+        $criteria->expects($this->once())
+            ->method('setRequestName')
+            ->with('advanced_search_container');
+
+        $tempTable = $this->getMock('Magento\Framework\DB\Ddl\Table', [], [], '', false);
+        $temporaryStorage = $this->getMock(
+            'Magento\Framework\Search\Adapter\Mysql\TemporaryStorage',
+            [],
+            [],
+            '',
+            false
+        );
+        $temporaryStorage->expects($this->once())->method('storeApiDocuments')->willReturn($tempTable);
+        $this->temporaryStorageFactory->expects($this->once())->method('create')->willReturn($temporaryStorage);
+        $searchResult = $this->getMock('Magento\Framework\Api\Search\SearchResultInterface', [], [], '', false);
+        $this->search->expects($this->once())->method('search')->willReturn($searchResult);
+
+        // addFieldsToFilter will load filters,
+        //   then loadWithFilter will trigger _renderFiltersBefore code in Advanced/Collection
+        $this->assertSame(
+            $this->advancedCollection,
+            $this->advancedCollection->addFieldsToFilter($filtersData)->loadWithFilter()
+        );
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function getCriteriaBuilder()
+    {
+        $criteriaBuilder = $this->getMockBuilder('Magento\Framework\Api\Search\SearchCriteriaBuilder')
+            ->setMethods(['addFilter', 'create', 'setRequestName'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        return $criteriaBuilder;
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d4ba4eef15f9a302ee547f31859fc0f2a90a8ff
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel;
+
+/**
+ * Base class for Collection tests.
+ *
+ * Contains helper methods to get commonly used mocks used for collection tests.
+ **/
+class BaseCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Get Mocks for StoreManager so Collection can be used.
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function getStoreManager()
+    {
+        $store = $this->getMockBuilder('Magento\Store\Model\Store')
+            ->setMethods(['getId'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $store->expects($this->once())
+            ->method('getId')
+            ->willReturn(1);
+
+        $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface')
+            ->setMethods(['getStore'])
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $storeManager->expects($this->once())
+            ->method('getStore')
+            ->willReturn($store);
+
+        return $storeManager;
+    }
+
+    /**
+     * Get mock for UniversalFactory so Collection can be used.
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function getUniversalFactory()
+    {
+        $connection = $this->getMockBuilder('Magento\Framework\DB\Adapter\Pdo\Mysql')
+            ->disableOriginalConstructor()
+            ->setMethods(['select'])
+            ->getMockForAbstractClass();
+        $select = $this->getMockBuilder('Magento\Framework\DB\Select')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $connection->expects($this->any())->method('select')->willReturn($select);
+
+        $entity = $this->getMockBuilder('Magento\Eav\Model\Entity\AbstractEntity')
+            ->setMethods(['getConnection', 'getTable', 'getDefaultAttributes', 'getEntityTable'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $entity->expects($this->once())
+            ->method('getConnection')
+            ->willReturn($connection);
+        $entity->expects($this->exactly(2))
+            ->method('getTable')
+            ->willReturnArgument(0);
+        $entity->expects($this->once())
+            ->method('getDefaultAttributes')
+            ->willReturn(['attr1', 'attr2']);
+        $entity->expects($this->once())
+            ->method('getEntityTable')
+            ->willReturn('table');
+
+        $universalFactory = $this->getMockBuilder('Magento\Framework\Validator\UniversalFactory')
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $universalFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($entity);
+
+        return $universalFactory;
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
index 616fa2c937b637a9d07f1c62710ef89b3c92260d..800b3a2ca311b2559c382a49cfe09b2e96a1775a 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
@@ -5,16 +5,20 @@
  */
 namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel\Fulltext;
 
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
-use PHPUnit_Framework_TestCase;
+use Magento\CatalogSearch\Test\Unit\Model\ResourceModel\BaseCollectionTest;
 
-class CollectionTest extends PHPUnit_Framework_TestCase
+class CollectionTest extends BaseCollectionTest
 {
     /**
      * @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection
      */
     private $model;
 
+    /**
+     * @var \Magento\Framework\Api\Filter
+     */
+    private $filter;
+
     /**
      * setUp method for CollectionTest
      */
@@ -25,7 +29,8 @@ class CollectionTest extends PHPUnit_Framework_TestCase
         $storeManager = $this->getStoreManager();
         $universalFactory = $this->getUniversalFactory();
         $scopeConfig = $this->getScopeConfig();
-        $requestBuilder = $this->getRequestBuilder();
+        $criteriaBuilder = $this->getCriteriaBuilder();
+        $filterBuilder = $this->getFilterBuilder();
 
         $this->model = $helper->getObject(
             'Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection',
@@ -33,9 +38,14 @@ class CollectionTest extends PHPUnit_Framework_TestCase
                 'storeManager' => $storeManager,
                 'universalFactory' => $universalFactory,
                 'scopeConfig' => $scopeConfig,
-                'requestBuilder' => $requestBuilder
             ]
         );
+
+        $search = $this->getMockForAbstractClass('\Magento\Search\Api\SearchInterface');
+        $this->model->setSearchCriteriaBuilder($criteriaBuilder);
+        $this->model->setSearch($search);
+        $this->model->setFilterBuilder($filterBuilder);
+
     }
 
     /**
@@ -48,72 +58,6 @@ class CollectionTest extends PHPUnit_Framework_TestCase
         $this->model->getFacetedData('field');
     }
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected function getStoreManager()
-    {
-        $store = $this->getMockBuilder('Magento\Store\Model\Store')
-            ->setMethods(['getId'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $store->expects($this->once())
-            ->method('getId')
-            ->willReturn(1);
-
-        $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface')
-            ->setMethods(['getStore'])
-            ->disableOriginalConstructor()
-            ->getMockForAbstractClass();
-        $storeManager->expects($this->once())
-            ->method('getStore')
-            ->willReturn($store);
-
-        return $storeManager;
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected function getUniversalFactory()
-    {
-        $connection = $this->getMockBuilder('Magento\Framework\DB\Adapter\Pdo\Mysql')
-            ->disableOriginalConstructor()
-            ->setMethods(['select'])
-            ->getMockForAbstractClass();
-        $select = $this->getMockBuilder('Magento\Framework\DB\Select')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $connection->expects($this->any())->method('select')->willReturn($select);
-
-        $entity = $this->getMockBuilder('Magento\Eav\Model\Entity\AbstractEntity')
-            ->setMethods(['getConnection', 'getTable', 'getDefaultAttributes', 'getEntityTable'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $entity->expects($this->once())
-            ->method('getConnection')
-            ->willReturn($connection);
-        $entity->expects($this->exactly(2))
-            ->method('getTable')
-            ->willReturnArgument(0);
-        $entity->expects($this->once())
-            ->method('getDefaultAttributes')
-            ->willReturn(['attr1', 'attr2']);
-        $entity->expects($this->once())
-            ->method('getEntityTable')
-            ->willReturn('table');
-
-        $universalFactory = $this->getMockBuilder('Magento\Framework\Validator\UniversalFactory')
-            ->setMethods(['create'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $universalFactory->expects($this->once())
-            ->method('create')
-            ->willReturn($entity);
-
-        return $universalFactory;
-    }
-
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
@@ -133,20 +77,37 @@ class CollectionTest extends PHPUnit_Framework_TestCase
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getRequestBuilder()
+    protected function getCriteriaBuilder()
     {
-        $requestBuilder = $this->getMockBuilder('Magento\Framework\Search\Request\Builder')
-            ->setMethods(['bind', 'setRequestName'])
+        $criteriaBuilder = $this->getMockBuilder('Magento\Framework\Api\Search\SearchCriteriaBuilder')
+            ->setMethods(['addFilter', 'create', 'setRequestName'])
             ->disableOriginalConstructor()
             ->getMock();
-        $requestBuilder->expects($this->once())
-            ->method('bind')
-            ->withConsecutive(['price_dynamic_algorithm', 1]);
-        $requestBuilder->expects($this->once())
+        $this->filter = new \Magento\Framework\Api\Filter();
+        $this->filter->setField('price_dynamic_algorithm');
+        $this->filter->setValue(1);
+        $criteriaBuilder->expects($this->once())
+            ->method('addFilter')
+            ->with($this->filter);
+        $criteria = $this->getMock('Magento\Framework\Api\Search\SearchCriteria', [], [], '', false);
+        $criteriaBuilder->expects($this->once())->method('create')->willReturn($criteria);
+        $criteria->expects($this->once())
             ->method('setRequestName')
             ->withConsecutive(['catalog_view_container'])
             ->willThrowException(new \Exception('setRequestName', 333));
 
-        return $requestBuilder;
+        return $criteriaBuilder;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function getFilterBuilder()
+    {
+        $filterBuilder = $this->getMock('Magento\Framework\Api\FilterBuilder', [], [], '', false);
+        $filterBuilder->expects($this->once())->method('setField')->with('price_dynamic_algorithm');
+        $filterBuilder->expects($this->once())->method('setValue')->with(1);
+        $filterBuilder->expects($this->once())->method('create')->willReturn($this->filter);
+        return $filterBuilder;
     }
 }
diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
index 9d5a35b0cc3318bf9fceaccc594e2fb35079bc53..46973ff5cfcc743a36f1a43f4d838f148d4d2e15 100644
--- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
+++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
@@ -171,7 +171,7 @@
             </argument>
         </field>
     </fieldset>
-    <fieldset name="websites">
+    <fieldset name="websites" class="Magento\Store\Ui\Component\Form\Fieldset\Websites">
         <argument name="data" xsi:type="array">
             <item name="config" xsi:type="array">
                 <item name="collapsible" xsi:type="boolean">true</item>
diff --git a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php
index 40e6653aab6381f562f24d3c1a53d8dde2c0a81c..d8d8211951292c8494ada0d1f1adb35d9c3bc95d 100644
--- a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php
+++ b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php
@@ -303,7 +303,7 @@ class Matrix extends \Magento\Backend\Block\Template
                             ];
                             foreach ($attribute->getOptions() as $option) {
                                 if (!empty($option->getValue())) {
-                                    $attributes[$attribute->getAttributeId()]['options'][$option->getValue()] = [
+                                    $attributes[$attribute->getAttributeId()]['options'][] = [
                                         'attribute_code' => $attribute->getAttributeCode(),
                                         'attribute_label' => $attribute->getStoreLabel(0),
                                         'id' => $option->getValue(),
@@ -322,7 +322,7 @@ class Matrix extends \Magento\Backend\Block\Template
                             'value' => $optionId,
                         ];
                         $variationOptions[] = $variationOption;
-                        $attributes[$attribute->getAttributeId()]['chosen'][$optionId] = $variationOption;
+                        $attributes[$attribute->getAttributeId()]['chosen'][] = $variationOption;
                     }
 
                     $productMatrix[] = [
diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
index f62703cc75d2763171ac030891d37ab49f77f79b..a052a70c1e9104a416641c5c5ad4d4541f249358 100644
--- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
+++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
@@ -51,7 +51,7 @@ class Plugin
             $attributes = $request->getParam('attributes');
             if (!empty($attributes)) {
                 $product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-                $this->configurableType->setUsedProductAttributeIds($attributes, $product);
+                $this->configurableType->setUsedProductAttributes($product, $attributes);
             } else {
                 $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE);
             }
@@ -73,7 +73,9 @@ class Plugin
             && $request->getParam('id', false) === false
         ) {
             $configProduct = $this->productFactory->create();
-            $configProduct->setStoreId(0)->load($request->getParam('product'))->setTypeId($request->getParam('type'));
+            $configProduct->setStoreId(0)
+                ->load($request->getParam('product'))
+                ->setTypeId($request->getParam('type'));
 
             $data = [];
             foreach ($configProduct->getTypeInstance()->getEditableAttributes($configProduct) as $attribute) {
diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php
index ee09e6ae6a221471286b25e6974ca1054f1455c9..1d0ee47fea955d048e940b455819c6d20fa76453 100644
--- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php
@@ -1,75 +1,121 @@
 <?php
 /**
- * Product initialzation helper
+ * Product initialization helper
  *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
 namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin;
 
+use Magento\Catalog\Api\Data\ProductExtensionInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper;
+use Magento\ConfigurableProduct\Helper\Product\Options\Factory;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableProduct;
+use Magento\ConfigurableProduct\Model\Product\VariationHandler;
+use Magento\Framework\App\RequestInterface;
 
+/**
+ * Class Configurable
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class Configurable
 {
-    /** @var \Magento\ConfigurableProduct\Model\Product\VariationHandler */
-    protected $variationHandler;
+    /**
+     * @var VariationHandler
+     */
+    private $variationHandler;
 
-    /** @var \Magento\Framework\App\RequestInterface */
-    protected $request;
+    /**
+     * @var RequestInterface
+     */
+    private $request;
 
-    /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable */
-    protected $productType;
+    /**
+     * @var Factory
+     */
+    private $optionsFactory;
 
     /**
-     * @param \Magento\ConfigurableProduct\Model\Product\VariationHandler $variationHandler
-     * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $productType
-     * @param \Magento\Framework\App\RequestInterface $request
+     * Constructor
+     *
+     * @param VariationHandler $variationHandler
+     * @param RequestInterface $request
+     * @param Factory $optionsFactory
      */
     public function __construct(
-        \Magento\ConfigurableProduct\Model\Product\VariationHandler $variationHandler,
-        \Magento\ConfigurableProduct\Model\Product\Type\Configurable $productType,
-        \Magento\Framework\App\RequestInterface $request
+        VariationHandler $variationHandler,
+        RequestInterface $request,
+        Factory $optionsFactory
     ) {
         $this->variationHandler = $variationHandler;
-        $this->productType = $productType;
         $this->request = $request;
+        $this->optionsFactory = $optionsFactory;
     }
 
     /**
      * Initialize data for configurable product
      *
-     * @param \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $subject
-     * @param \Magento\Catalog\Model\Product $product
+     * @param Helper $subject
+     * @param ProductInterface $product
+     * @return ProductInterface
+     * @throws \InvalidArgumentException
      *
-     * @return \Magento\Catalog\Model\Product
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function afterInitialize(
-        \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $subject,
-        \Magento\Catalog\Model\Product $product
-    ) {
+    public function afterInitialize(Helper $subject, ProductInterface $product)
+    {
         $attributes = $this->request->getParam('attributes');
-        if ($product->getTypeId() == ConfigurableProduct::TYPE_CODE && !empty($attributes)) {
-            $setId = $this->request->getPost('new-variations-attribute-set-id');
-            if ($setId) {
-                $product->setAttributeSetId($setId);
-            }
-            $this->productType->setUsedProductAttributeIds($attributes, $product);
+        $productData = $this->request->getPost('product', []);
+
+        if ($product->getTypeId() !== ConfigurableProduct::TYPE_CODE || empty($attributes)) {
+            return $product;
+        }
+
+        $setId = $this->request->getPost('new-variations-attribute-set-id');
+        if ($setId) {
+            $product->setAttributeSetId($setId);
+        }
+        $extensionAttributes = $product->getExtensionAttributes();
 
-            $product->setNewVariationsAttributeSetId($setId);
-            $associatedProductIds = $this->request->getPost('associated_product_ids', []);
-            $variationsMatrix = $this->request->getParam('variations-matrix', []);
-            if (!empty($variationsMatrix)) {
-                $generatedProductIds = $this->variationHandler->generateSimpleProducts($product, $variationsMatrix);
-                $associatedProductIds = array_merge($associatedProductIds, $generatedProductIds);
-            }
-            $product->setAssociatedProductIds(array_filter($associatedProductIds));
+        $product->setNewVariationsAttributeSetId($setId);
 
-            $product->setCanSaveConfigurableAttributes(
-                (bool)$this->request->getPost('affect_configurable_product_attributes')
+        $configurableOptions = [];
+        if (!empty($productData['configurable_attributes_data'])) {
+            $configurableOptions = $this->optionsFactory->create(
+                (array) $productData['configurable_attributes_data']
             );
         }
 
+        $extensionAttributes->setConfigurableProductOptions($configurableOptions);
+
+        $this->setLinkedProducts($product, $extensionAttributes);
+        $product->setCanSaveConfigurableAttributes(
+            (bool) $this->request->getPost('affect_configurable_product_attributes')
+        );
+
+        $product->setExtensionAttributes($extensionAttributes);
+
         return $product;
     }
+
+    /**
+     * Relate simple products to configurable
+     *
+     * @param ProductInterface $product
+     * @param ProductExtensionInterface $extensionAttributes
+     * @return void
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    private function setLinkedProducts(ProductInterface $product, ProductExtensionInterface $extensionAttributes)
+    {
+        $associatedProductIds = $this->request->getPost('associated_product_ids', []);
+        $variationsMatrix = $this->request->getParam('variations-matrix', []);
+        if (!empty($variationsMatrix)) {
+            $generatedProductIds = $this->variationHandler->generateSimpleProducts($product, $variationsMatrix);
+            $associatedProductIds = array_merge($associatedProductIds, $generatedProductIds);
+        }
+        $extensionAttributes->setConfigurableProductLinks(array_filter($associatedProductIds));
+    }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ecdf85d2634627d8d49a6d0c1865573f3249beb
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Helper\Product\Options;
+
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory;
+
+/**
+ * Class Factory
+ */
+class Factory
+{
+    /**
+     * @var AttributeFactory
+     */
+    private $attributeFactory;
+
+    /**
+     * @var ProductAttributeRepositoryInterface
+     */
+    private $productAttributeRepository;
+
+    /**
+     * @var Configurable
+     */
+    private $productType;
+
+    /**
+     * @var OptionValueInterfaceFactory
+     */
+    private $optionValueFactory;
+
+    /**
+     * Constructor
+     *
+     * @param Configurable $productType
+     * @param AttributeFactory $attributeFactory
+     * @param OptionValueInterfaceFactory $optionValueFactory
+     * @param ProductAttributeRepositoryInterface $productAttributeRepository
+     */
+    public function __construct(
+        Configurable $productType,
+        AttributeFactory $attributeFactory,
+        OptionValueInterfaceFactory $optionValueFactory,
+        ProductAttributeRepositoryInterface $productAttributeRepository
+    ) {
+        $this->productType = $productType;
+        $this->attributeFactory = $attributeFactory;
+        $this->optionValueFactory = $optionValueFactory;
+        $this->productAttributeRepository = $productAttributeRepository;
+    }
+
+    /**
+     * Create configurable product options
+     *
+     * @param array $attributesData
+     * @return OptionInterface[]
+     * @throws \InvalidArgumentException
+     */
+    public function create(array $attributesData)
+    {
+        $options = [];
+
+        foreach ($attributesData as $item) {
+            $attribute = $this->attributeFactory->create();
+            $eavAttribute = $this->productAttributeRepository->get($item[Attribute::KEY_ATTRIBUTE_ID]);
+
+            if (!$this->productType->canUseAttribute($eavAttribute)) {
+                throw new \InvalidArgumentException('Provided attribute can not be used with configurable product.');
+            }
+
+            $this->updateAttributeData($attribute, $item);
+            $options[] = $attribute;
+        }
+
+        return $options;
+    }
+
+    /**
+     * Update attribute data
+     *
+     * @param OptionInterface $attribute
+     * @param array $item
+     * @return void
+     */
+    private function updateAttributeData(OptionInterface $attribute, array $item)
+    {
+        $values = [];
+        foreach ($item['values'] as $value) {
+            $option = $this->optionValueFactory->create();
+            $option->setValueIndex($value['value_index']);
+            $values[] = $option;
+        }
+        $attribute->setData(
+            array_replace_recursive(
+                (array)$attribute->getData(),
+                $item
+            )
+        );
+        $attribute->setValues($values);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php
new file mode 100644
index 0000000000000000000000000000000000000000..e71e7ccf2f377261b46efc22b974db0d25d22d39
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Helper\Product\Options;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory;
+
+/**
+ * Class Loader
+ */
+class Loader
+{
+    /**
+     * @var OptionValueInterfaceFactory
+     */
+    private $optionValueFactory;
+
+    /**
+     * ReadHandler constructor
+     *
+     * @param OptionValueInterfaceFactory $optionValueFactory
+     */
+    public function __construct(OptionValueInterfaceFactory $optionValueFactory)
+    {
+        $this->optionValueFactory = $optionValueFactory;
+    }
+
+    /**
+     * @param ProductInterface $product
+     * @return OptionInterface[]
+     */
+    public function load(ProductInterface $product)
+    {
+        $options = [];
+        /** @var Configurable $typeInstance */
+        $typeInstance = $product->getTypeInstance();
+        $attributeCollection = $typeInstance->getConfigurableAttributes($product);
+
+        foreach ($attributeCollection as $attribute) {
+            $values = [];
+            $attributeOptions = $attribute->getOptions();
+            if (is_array($attributeOptions)) {
+                foreach ($attributeOptions as $option) {
+                    /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */
+                    $value = $this->optionValueFactory->create();
+                    $value->setValueIndex($option['value_index']);
+                    $values[] = $value;
+                }
+            }
+            $attribute->setValues($values);
+            $options[] = $attribute;
+        }
+
+        return $options;
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php b/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php
index 059b7e1f9bfd18ced6418d331bc703f8a43c51e5..4b086658d70f9af8bf9cd7b84417c76eec65478f 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php
@@ -5,9 +5,14 @@
  */
 namespace Magento\ConfigurableProduct\Model\Attribute;
 
-use Magento\Catalog\Model\Attribute\LockValidatorInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Model\Entity\MetadataPool;
+use Magento\Catalog\Model\Attribute\LockValidatorInterface;
 
+/**
+ * Class LockValidator
+ */
 class LockValidator implements LockValidatorInterface
 {
     /**
@@ -16,11 +21,22 @@ class LockValidator implements LockValidatorInterface
     protected $resource;
 
     /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * Constructor
+     *
      * @param ResourceConnection $resource
+     * @param MetadataPool $metadataPool
      */
-    public function __construct(ResourceConnection $resource)
-    {
+    public function __construct(
+        ResourceConnection $resource,
+        MetadataPool $metadataPool
+    ) {
         $this->resource = $resource;
+        $this->metadataPool = $metadataPool;
     }
 
     /**
@@ -33,25 +49,22 @@ class LockValidator implements LockValidatorInterface
      */
     public function validate(\Magento\Framework\Model\AbstractModel $object, $attributeSet = null)
     {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         $connection = $this->resource->getConnection();
-        $attrTable = $this->resource->getTableName('catalog_product_super_attribute');
-        $productTable = $this->resource->getTableName('catalog_product_entity');
 
         $bind = ['attribute_id' => $object->getAttributeId()];
+
         $select = clone $connection->select();
-        $select->reset()->from(
-            ['main_table' => $attrTable],
-            ['psa_count' => 'COUNT(product_super_attribute_id)']
-        )->join(
-            ['entity' => $productTable],
-            'main_table.product_id = entity.entity_id'
-        )->where(
-            'main_table.attribute_id = :attribute_id'
-        )->group(
-            'main_table.attribute_id'
-        )->limit(
-            1
-        );
+        $select->reset()
+            ->from(
+                ['main_table' => $this->resource->getTableName('catalog_product_super_attribute')],
+                ['psa_count' => 'COUNT(product_super_attribute_id)']
+            )->join(
+                ['entity' => $this->resource->getTableName('catalog_product_entity')],
+                'main_table.product_id = entity.' . $metadata->getLinkField()
+            )->where('main_table.attribute_id = :attribute_id')
+            ->group('main_table.attribute_id')
+            ->limit(1);
 
         if ($attributeSet !== null) {
             $bind['attribute_set_id'] = $attributeSet;
diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
index d1e08f2367c3518e83a6a6fb92c46edc15af6f67..27470ce03797b16d5bc20b35b2da892195fb321c 100644
--- a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
+++ b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
@@ -9,6 +9,7 @@ namespace Magento\ConfigurableProduct\Model;
 use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Catalog\Model\Product;
 use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\ConfigurableProduct\Helper\Product\Options\Loader;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Framework\Exception\StateException;
 use Magento\Framework\Exception\InputException;
@@ -16,6 +17,7 @@ use Magento\Framework\Exception\CouldNotSaveException;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType;
 use Magento\Catalog\Model\Product\Type as ProductType;
 use Magento\Store\Model\Store;
+use Magento\Framework\Model\Entity\MetadataPool;
 
 /**
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -62,6 +64,16 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
      */
     private $configurableTypeResource;
 
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * @var Loader
+     */
+    private $optionLoader;
+
     /**
      * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
      * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory
@@ -71,6 +83,10 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
      * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository
      * @param ConfigurableType\AttributeFactory $configurableAttributeFactory
      * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource
+     * @param MetadataPool $metadataPool
+     * @param Loader $optionLoader
+     *
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
         \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
@@ -80,7 +96,9 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository,
         \Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory $configurableAttributeFactory,
-        \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource
+        \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource,
+        MetadataPool $metadataPool,
+        Loader $optionLoader
     ) {
         $this->productRepository = $productRepository;
         $this->optionValueFactory = $optionValueFactory;
@@ -90,6 +108,8 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
         $this->productAttributeRepository = $productAttributeRepository;
         $this->configurableAttributeFactory = $configurableAttributeFactory;
         $this->configurableTypeResource = $configurableTypeResource;
+        $this->metadataPool = $metadataPool;
+        $this->optionLoader = $optionLoader;
     }
 
     /**
@@ -99,15 +119,13 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
     {
         $product = $this->getProduct($sku);
 
-        $extensionAttribute = $product->getExtensionAttributes();
-        if ($extensionAttribute !== null) {
-            $options = $extensionAttribute->getConfigurableProductOptions();
-            foreach ($options as $option) {
-                if ($option->getId() == $id) {
-                    return $option;
-                }
+        $options = $this->optionLoader->load($product);
+        foreach ($options as $option) {
+            if ($option->getId() == $id) {
+                return $option;
             }
         }
+
         throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id));
     }
 
@@ -116,14 +134,9 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
      */
     public function getList($sku)
     {
-        $options = [];
         $product = $this->getProduct($sku);
 
-        $extensionAttribute = $product->getExtensionAttributes();
-        if ($extensionAttribute !== null) {
-            $options = $extensionAttribute->getConfigurableProductOptions();
-        }
-        return $options;
+        return (array) $this->optionLoader->load($product);
     }
 
     /**
@@ -131,12 +144,14 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
      */
     public function delete(OptionInterface $option)
     {
-        $product = $this->getProductById($option->getProductId());
+        $entityId = $this->configurableTypeResource->getEntityIdByAttribute($option);
+        $product = $this->getProductById($entityId);
+
         try {
             $this->configurableTypeResource->saveProducts($product, []);
         } catch (\Exception $exception) {
             throw new StateException(
-                __('Cannot delete variations from product: %1', $option->getProductId())
+                __('Cannot delete variations from product: %1', $entityId)
             );
         }
         try {
@@ -170,13 +185,14 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
      */
     public function save($sku, OptionInterface $option)
     {
-        /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */
-        $configurableAttribute = $this->configurableAttributeFactory->create();
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         if ($option->getId()) {
             /** @var Product $product */
             $product = $this->getProduct($sku);
-            $configurableAttribute->load($option->getId());
-            if (!$configurableAttribute->getId() || $configurableAttribute->getProductId() != $product->getId()) {
+            $data = $option->getData();
+            $option->load($option->getId());
+            $option->setData(array_replace_recursive($option->getData(), $data));
+            if (!$option->getId() || $option->getProductId() != $product->getData($metadata->getLinkField())) {
                 throw new NoSuchEntityException(
                     __(
                         'Option with id "%1" not found',
@@ -184,60 +200,27 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
                     )
                 );
             }
-            $configurableAttribute->addData($option->getData());
-            $configurableAttribute->setValues(
-                $option->getValues() !== null ? $option->getValues() : $configurableAttribute->getOptions()
-            );
-
-            try {
-                $configurableAttribute->save();
-            } catch (\Exception $e) {
-                throw new CouldNotSaveException(
-                    __(
-                        'Could not update option with id "%1"',
-                        $option->getId()
-                    )
-                );
-            }
         } else {
-            $this->validateNewOptionData($option);
             /** @var Product $product */
             $product = $this->productRepository->get($sku);
+            $this->validateNewOptionData($option);
             $allowedTypes = [ProductType::TYPE_SIMPLE, ProductType::TYPE_VIRTUAL, ConfigurableType::TYPE_CODE];
             if (!in_array($product->getTypeId(), $allowedTypes)) {
                 throw new \InvalidArgumentException('Incompatible product type');
             }
+            $option->setProductId($product->getData($metadata->getLinkField()));
+        }
 
-            $eavAttribute = $this->productAttributeRepository->get($option->getAttributeId());
-            $configurableAttribute->loadByProductAndAttribute($product, $eavAttribute);
-            if ($configurableAttribute->getId()) {
-                throw new CouldNotSaveException(__('Product already has this option'));
-            }
-
-            $configurableAttributesData = [
-                'attribute_id' => $option->getAttributeId(),
-                'position' => $option->getPosition(),
-                'use_default' => $option->getIsUseDefault(),
-                'label' => $option->getLabel(),
-                'values' => $option->getValues()
-            ];
-
-            try {
-                $product->setTypeId(ConfigurableType::TYPE_CODE);
-                $product->setConfigurableAttributesData([$configurableAttributesData]);
-                $product->setStoreId($this->storeManager->getStore(Store::ADMIN_CODE)->getId());
-                $product->save();
-            } catch (\Exception $e) {
-                throw new CouldNotSaveException(__('Something went wrong while saving option.'));
-            }
-
-            $configurableAttribute = $this->configurableAttributeFactory->create();
-            $configurableAttribute->loadByProductAndAttribute($product, $eavAttribute);
+        try {
+            $option->save();
+        } catch (\Exception $e) {
+            throw new CouldNotSaveException(__('Something went wrong while saving option.'));
         }
-        if (!$configurableAttribute->getId()) {
+
+        if (!$option->getId()) {
             throw new CouldNotSaveException(__('Something went wrong while saving option.'));
         }
-        return $configurableAttribute->getId();
+        return $option->getId();
     }
 
     /**
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php
deleted file mode 100644
index 2d04a2cda28b87c7baf2de3d78f1a5c4c3b4939d..0000000000000000000000000000000000000000
--- a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\ConfigurableProduct\Model\Plugin;
-
-class AfterProductLoad
-{
-    /**
-     * @var \Magento\Catalog\Api\Data\ProductExtensionFactory
-     */
-    protected $productExtensionFactory;
-
-    /**
-     * @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory
-     */
-    protected $optionValueFactory;
-
-    /**
-     * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
-     * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory
-     */
-    public function __construct(
-        \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory,
-        \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory
-    ) {
-        $this->productExtensionFactory = $productExtensionFactory;
-        $this->optionValueFactory = $optionValueFactory;
-    }
-
-    /**
-     * @param \Magento\Catalog\Model\Product $product
-     * @return \Magento\Catalog\Model\Product
-     */
-    public function afterLoad(\Magento\Catalog\Model\Product $product)
-    {
-        if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) {
-            return $product;
-        }
-
-        $productExtension = $product->getExtensionAttributes();
-        if ($productExtension === null) {
-            $productExtension = $this->productExtensionFactory->create();
-        }
-
-        $productExtension->setConfigurableProductOptions($this->getOptions($product));
-        $productExtension->setConfigurableProductLinks($this->getLinkedProducts($product));
-
-        $product->setExtensionAttributes($productExtension);
-
-        return $product;
-    }
-
-    /**
-     * @param \Magento\Catalog\Model\Product $product
-     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]
-     */
-    protected function getOptions(\Magento\Catalog\Model\Product $product)
-    {
-        $options = [];
-        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */
-        $typeInstance = $product->getTypeInstance();
-        $attributeCollection = $typeInstance->getConfigurableAttributes($product);
-        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $option */
-        foreach ($attributeCollection as $attribute) {
-            $values = [];
-            $attributeOptions = $attribute->getOptions();
-            if (is_array($attributeOptions)) {
-                foreach ($attributeOptions as $option) {
-                    /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */
-                    $value = $this->optionValueFactory->create();
-                    $value->setValueIndex($option['value_index']);
-                    $values[] = $value;
-                }
-            }
-            $attribute->setValues($values);
-            $options[] = $attribute;
-        }
-        return $options;
-    }
-
-    /**
-     * @param \Magento\Catalog\Model\Product $product
-     * @return int[]
-     */
-    protected function getLinkedProducts(\Magento\Catalog\Model\Product $product)
-    {
-        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */
-        $typeInstance = $product->getTypeInstance();
-        $childrenIds = $typeInstance->getChildrenIds($product->getId());
-
-        if (isset($childrenIds[0])) {
-            return $childrenIds[0];
-        } else {
-            return [];
-        }
-    }
-}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
index de7898af9a19c70c1f4c0025b031e1bff8a79f74..266a8eb172adeec3b1466f00a9e4e81bc2fcdd7e 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
@@ -1,172 +1,105 @@
 <?php
 /**
- *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\ConfigurableProduct\Model\Plugin;
 
+use Magento\Catalog\Model\ProductFactory;
+use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Framework\Exception\InputException;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\Exception\CouldNotSaveException;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 
+/**
+ * Class AroundProductRepositorySave
+ */
 class AroundProductRepositorySave
 {
     /**
-     * @var \Magento\ConfigurableProduct\Api\OptionRepositoryInterface
-     */
-    protected $optionRepository;
-
-    /**
-     * @var \Magento\Catalog\Model\ProductFactory
+     * @var ProductAttributeRepositoryInterface
      */
-    protected $productFactory;
+    private $productAttributeRepository;
 
     /**
-     * Type configurable factory
-     *
-     * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory
+     * @var ProductFactory
      */
-    protected $typeConfigurableFactory;
+    private $productFactory;
 
     /**
-     * @param \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository
-     * @param \Magento\Catalog\Model\ProductFactory $productFactory
-     * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory $typeConfigurableFactory
+     * @param ProductAttributeRepositoryInterface $productAttributeRepository
+     * @param ProductFactory $productFactory
      */
     public function __construct(
-        \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository,
-        \Magento\Catalog\Model\ProductFactory $productFactory,
-        \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory $typeConfigurableFactory
+        ProductAttributeRepositoryInterface $productAttributeRepository,
+        ProductFactory $productFactory
     ) {
-        $this->optionRepository = $optionRepository;
+        $this->productAttributeRepository = $productAttributeRepository;
         $this->productFactory = $productFactory;
-        $this->typeConfigurableFactory = $typeConfigurableFactory;
     }
 
     /**
-     * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
+     * @param ProductRepositoryInterface $subject
      * @param callable $proceed
-     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param ProductInterface $product
      * @param bool $saveOptions
-     * @return \Magento\Catalog\Api\Data\ProductInterface
-     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @return ProductInterface
+     * @throws CouldNotSaveException
+     * @throws InputException
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function aroundSave(
-        \Magento\Catalog\Api\ProductRepositoryInterface $subject,
+        ProductRepositoryInterface $subject,
         \Closure $proceed,
-        \Magento\Catalog\Api\Data\ProductInterface $product,
+        ProductInterface $product,
         $saveOptions = false
     ) {
-        /** @var \Magento\Catalog\Api\Data\ProductInterface $result */
+        /** @var ProductInterface $result */
         $result = $proceed($product, $saveOptions);
-
-        if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) {
+        if ($product->getTypeId() !== Configurable::TYPE_CODE) {
             return $result;
         }
 
-        $extendedAttributes = $product->getExtensionAttributes();
-        if ($extendedAttributes === null) {
+        $extensionAttributes = $result->getExtensionAttributes();
+        if ($extensionAttributes === null) {
             return $result;
         }
-        $configurableProductOptions = $extendedAttributes->getConfigurableProductOptions();
-        $configurableProductLinks = $extendedAttributes->getConfigurableProductLinks();
-        if ($configurableProductOptions === null && $configurableProductLinks === null) {
-            return $result;
-        }
-        if ($configurableProductOptions !== null) {
-            $this->saveConfigurableProductOptions($result, $configurableProductOptions);
-            $result->getTypeInstance()->resetConfigurableAttributes($result);
-        }
-        if ($configurableProductLinks !== null) {
-            $this->saveConfigurableProductLinks($result, $configurableProductLinks);
-        }
-        return $subject->get($result->getSku(), false, $result->getStoreId(), true);
-    }
-
-    /**
-     * @param \Magento\Catalog\Api\Data\ProductInterface $product
-     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $options
-     * @return $this
-     */
-    protected function saveConfigurableProductOptions(
-        \Magento\Catalog\Api\Data\ProductInterface $product,
-        array $options
-    ) {
-        $existingOptionIds = [];
-        if ($product->getExtensionAttributes() !== null) {
-            $extensionAttributes = $product->getExtensionAttributes();
-            if ($extensionAttributes->getConfigurableProductOptions() !== null) {
-                $existingOptions = $extensionAttributes->getConfigurableProductOptions();
-                foreach ($existingOptions as $option) {
-                    $existingOptionIds[] = $option->getId();
-                }
-            }
-        }
 
-        $updatedOptionIds = [];
-        foreach ($options as $option) {
-            if ($option->getId()) {
-                $updatedOptionIds[] = $option->getId();
-            }
-            $this->optionRepository->save($product->getSku(), $option);
-        }
+        $configurableLinks = (array) $extensionAttributes->getConfigurableProductLinks();
+        $configurableOptions = (array) $extensionAttributes->getConfigurableProductOptions();
 
-        $optionIdsToDelete = array_diff($existingOptionIds, $updatedOptionIds);
-        foreach ($optionIdsToDelete as $optionId) {
-            $this->optionRepository->deleteById($product->getSku(), $optionId);
+        if (empty($configurableLinks) && empty($configurableOptions)) {
+            return $result;
         }
-        return $this;
-    }
 
-    /**
-     * @param \Magento\Catalog\Api\Data\ProductInterface $product
-     * @param int[] $linkIds
-     * @return $this
-     */
-    protected function saveConfigurableProductLinks(
-        \Magento\Catalog\Api\Data\ProductInterface $product,
-        array $linkIds
-    ) {
-        $configurableProductTypeResource = $this->typeConfigurableFactory->create();
-        if (!empty($linkIds)) {
-            /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableProductType */
-            $configurableProductType = $product->getTypeInstance();
-            $configurableAttributes = $configurableProductType->getConfigurableAttributes($product);
-            $attributeCodes = [];
-            foreach ($configurableAttributes as $configurableAttribute) {
-                /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $productAttribute */
-                $productAttribute = $configurableAttribute->getProductAttribute();
-                $attributeCode = $productAttribute->getAttributeCode();
-                $attributeCodes[] = $attributeCode;
-            }
-            $this->validateProductLinks($attributeCodes, $linkIds);
+        $attributeCodes = [];
+        /** @var OptionInterface $configurableOption */
+        foreach ($configurableOptions as $configurableOption) {
+            $eavAttribute = $this->productAttributeRepository->get($configurableOption->getAttributeId());
+            $attributeCode = $eavAttribute->getAttributeCode();
+            $attributeCodes[] = $attributeCode;
         }
+        $this->validateProductLinks($attributeCodes, $configurableLinks);
 
-        $configurableProductTypeResource->saveProducts($product, $linkIds);
-        return $this;
+        return $subject->get($result->getSku(), false, $result->getStoreId(), true);
     }
 
     /**
      * @param array $attributeCodes
      * @param array $linkIds
-     * @throws InputException
      * @return $this
+     * @throws InputException
      */
-    protected function validateProductLinks(array $attributeCodes, array $linkIds)
+    private function validateProductLinks(array $attributeCodes, array $linkIds)
     {
         $valueMap = [];
-        if (empty($attributeCodes) && !empty($linkIds)) {
-            throw new InputException(
-                __('The configurable product does not have any variation attribute.')
-            );
-        }
 
         foreach ($linkIds as $productId) {
             $variation = $this->productFactory->create()->load($productId);
-            if (!$variation->getId()) {
-                throw new InputException(__('Product with id "%1" does not exist.', $productId));
-            }
             $valueKey = '';
             foreach ($attributeCodes as $attributeCode) {
                 if (!$variation->getData($attributeCode)) {
@@ -178,11 +111,14 @@ class AroundProductRepositorySave
             }
             if (isset($valueMap[$valueKey])) {
                 throw new InputException(
-                    __('Products "%1" and %2 have the same set of attribute values.', $productId, $valueMap[$valueKey])
+                    __(
+                        'Products "%1" and "%2" have the same set of attribute values.',
+                        $productId,
+                        $valueMap[$valueKey]
+                    )
                 );
             }
             $valueMap[$valueKey] = $productId;
         }
-        return $this;
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..87d67b7ca334f3979c27a885490d70bde50a6ac9
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Model\Product;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\ConfigurableProduct\Helper\Product\Options\Loader;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+
+/**
+ * Class ReadHandler
+ */
+class ReadHandler
+{
+    /**
+     * @var Loader
+     */
+    private $optionLoader;
+
+    /**
+     * ReadHandler constructor
+     *
+     * @param Loader $optionLoader
+     */
+    public function __construct(Loader $optionLoader)
+    {
+        $this->optionLoader = $optionLoader;
+    }
+
+    /**
+     * @param string $entityType
+     * @param ProductInterface $entity
+     * @return ProductInterface
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function execute($entityType, $entity)
+    {
+        if ($entity->getTypeId() !== Configurable::TYPE_CODE) {
+            return $entity;
+        }
+
+        $extensionAttributes = $entity->getExtensionAttributes();
+
+        $extensionAttributes->setConfigurableProductLinks($this->getLinkedProducts($entity));
+        $extensionAttributes->setConfigurableProductOptions($this->optionLoader->load($entity));
+
+        $entity->setExtensionAttributes($extensionAttributes);
+
+        return $entity;
+    }
+
+    /**
+     * Get linked to configurable simple products
+     *
+     * @param ProductInterface $product
+     * @return int[]
+     */
+    private function getLinkedProducts(ProductInterface $product)
+    {
+        /** @var Configurable $typeInstance */
+        $typeInstance = $product->getTypeInstance();
+        $childrenIds = $typeInstance->getChildrenIds($product->getId());
+
+        if (isset($childrenIds[0])) {
+            return $childrenIds[0];
+        } else {
+            return [];
+        }
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a1c0c96b4c57f8234c62b7389e52d6ff57fb1cc
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Model\Product;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\ConfigurableProduct\Api\OptionRepositoryInterface;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable as ResourceModelConfigurable;
+
+/**
+ * Class SaveHandler
+ */
+class SaveHandler
+{
+    /**
+     * @var OptionRepositoryInterface
+     */
+    private $optionRepository;
+
+    /**
+     * @var ProductAttributeRepositoryInterface
+     */
+    private $productAttributeRepository;
+
+    /**
+     * @var ResourceModelConfigurable
+     */
+    private $resourceModel;
+
+    /**
+     * SaveHandler constructor
+     *
+     * @param ResourceModelConfigurable $resourceModel
+     * @param OptionRepositoryInterface $optionRepository
+     * @param ProductAttributeRepositoryInterface $productAttributeRepository
+     */
+    public function __construct(
+        ResourceModelConfigurable $resourceModel,
+        OptionRepositoryInterface $optionRepository,
+        ProductAttributeRepositoryInterface $productAttributeRepository
+    ) {
+        $this->resourceModel = $resourceModel;
+        $this->optionRepository = $optionRepository;
+        $this->productAttributeRepository = $productAttributeRepository;
+    }
+
+    /**
+     * @param string $entityType
+     * @param ProductInterface $entity
+     * @return ProductInterface
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function execute($entityType, ProductInterface $entity)
+    {
+        if ($entity->getTypeId() !== Configurable::TYPE_CODE) {
+            return $entity;
+        }
+
+        $extensionAttributes = $entity->getExtensionAttributes();
+        if ($extensionAttributes === null) {
+            return $entity;
+        }
+
+        $ids = [];
+        $configurableOptions = (array) $extensionAttributes->getConfigurableProductOptions();
+        if (!empty($configurableOptions)) {
+            $ids = $this->saveConfigurableProductAttributes($entity, $configurableOptions);
+        }
+
+        $configurableLinks = (array) $extensionAttributes->getConfigurableProductLinks();
+        $this->resourceModel->saveProducts($entity, $configurableLinks);
+        if (empty($configurableLinks) || !empty($ids)) {
+            $this->deleteConfigurableProductAttributes($entity, $ids);
+        }
+
+        return $entity;
+    }
+
+    /**
+     * Save attributes for configurable product
+     *
+     * @param ProductInterface $product
+     * @param array $attributes
+     * @return array
+     */
+    private function saveConfigurableProductAttributes(ProductInterface $product, array $attributes)
+    {
+        $ids = [];
+        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $attribute */
+        foreach ($attributes as $attribute) {
+            $eavAttribute = $this->productAttributeRepository->get($attribute->getAttributeId());
+
+            $data = $attribute->getData();
+            $attribute->loadByProductAndAttribute($product, $eavAttribute);
+            $attribute->setData(array_replace_recursive($attribute->getData(), $data));
+
+            $ids[] = $this->optionRepository->save($product->getSku(), $attribute);
+        }
+
+        return $ids;
+    }
+
+    /**
+     * Remove product attributes
+     *
+     * @param ProductInterface $product
+     * @param array $attributesIds
+     * @return void
+     */
+    private function deleteConfigurableProductAttributes(ProductInterface $product, array $attributesIds)
+    {
+        $list = $this->optionRepository->getList($product->getSku());
+        foreach ($list as $item) {
+            if (!in_array($item->getId(), $attributesIds)) {
+                $this->optionRepository->deleteById($product->getSku(), $item->getId());
+            }
+        }
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index 93f2588d214597ef54278c672963794b66d5d00b..727b090c73e5b1139436a539943954578ed22f35 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\ConfigurableProduct\Model\Product\Type;
 
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Framework\Model\Entity\MetadataPool;
 use Magento\Catalog\Api\ProductRepositoryInterface;
 
 /**
@@ -28,14 +30,14 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      *
      * @var string
      */
-    protected $_usedProductAttributeIds = '_cache_instance_used_product_attribute_ids';
+    protected $usedProductAttributeIds = '_cache_instance_used_product_attribute_ids';
 
     /**
      * Cache key for Used Product Attributes
      *
      * @var string
      */
-    protected $_usedProductAttributes = '_cache_instance_used_product_attributes';
+    protected $usedProductAttributes = '_cache_instance_used_product_attributes';
 
     /**
      * Cache key for Used Attributes
@@ -111,7 +113,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      *
      * @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory
      */
-    protected $_configurableAttributeFactory;
+    protected $configurableAttributeFactory;
 
     /**
      * Eav attribute factory
@@ -125,13 +127,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      *
      * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory
      */
-    protected $_typeConfigurableFactory;
+    protected $typeConfigurableFactory;
 
     /**
      * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface
      */
     protected $extensionAttributesJoinProcessor;
 
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
     /**
      * @codingStandardsIgnoreStart/End
      *
@@ -152,7 +159,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor
-     *
+     * @param MetadataPool $metadataPool
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -172,16 +179,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
         \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\CollectionFactory $attributeCollectionFactory,
         \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
-        \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor
+        \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor,
+        MetadataPool $metadataPool
     ) {
-        $this->_typeConfigurableFactory = $typeConfigurableFactory;
+        $this->typeConfigurableFactory = $typeConfigurableFactory;
         $this->_eavAttributeFactory = $eavAttributeFactory;
-        $this->_configurableAttributeFactory = $configurableAttributeFactory;
+        $this->configurableAttributeFactory = $configurableAttributeFactory;
         $this->_productCollectionFactory = $productCollectionFactory;
         $this->_attributeCollectionFactory = $attributeCollectionFactory;
         $this->_catalogProductTypeConfigurable = $catalogProductTypeConfigurable;
         $this->_scopeConfig = $scopeConfig;
         $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
+        $this->metadataPool = $metadataPool;
 
         parent::__construct(
             $catalogProductOption,
@@ -260,6 +269,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      * @param   array $ids
      * @param   \Magento\Catalog\Model\Product $product
      * @return  \Magento\ConfigurableProduct\Model\Product\Type\Configurable
+     * @deprecated use \Magento\ConfigurableProduct\Model\Product\Type\Configurable::setUsedProductAttributes instead
      */
     public function setUsedProductAttributeIds($ids, $product)
     {
@@ -268,17 +278,37 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
 
         foreach ($ids as $attributeId) {
             $usedProductAttributes[] = $this->getAttributeById($attributeId, $product);
-            $configurableAttributes[] = $this->_configurableAttributeFactory->create()->setProductAttribute(
+            $configurableAttributes[] = $this->configurableAttributeFactory->create()->setProductAttribute(
                 $this->getAttributeById($attributeId, $product)
             );
         }
-        $product->setData($this->_usedProductAttributes, $usedProductAttributes);
-        $product->setData($this->_usedProductAttributeIds, $ids);
+        $product->setData($this->usedProductAttributes, $usedProductAttributes);
+        $product->setData($this->usedProductAttributeIds, $ids);
         $product->setData($this->_configurableAttributes, $configurableAttributes);
 
         return $this;
     }
 
+    /**
+     * Set list of used attributes to product
+     *
+     * @param ProductInterface $product
+     * @param array $ids
+     * @return $this
+     */
+    public function setUsedProductAttributes(ProductInterface $product, array $ids)
+    {
+        $usedProductAttributes = [];
+
+        foreach ($ids as $attributeId) {
+            $usedProductAttributes[] = $this->getAttributeById($attributeId, $product);
+        }
+        $product->setData($this->usedProductAttributes, $usedProductAttributes);
+        $product->setData($this->usedProductAttributeIds, $ids);
+
+        return $this;
+    }
+
     /**
      * Retrieve identifiers of used product attributes
      *
@@ -287,14 +317,14 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      */
     public function getUsedProductAttributeIds($product)
     {
-        if (!$product->hasData($this->_usedProductAttributeIds)) {
+        if (!$product->hasData($this->usedProductAttributeIds)) {
             $usedProductAttributeIds = [];
             foreach ($this->getUsedProductAttributes($product) as $attribute) {
                 $usedProductAttributeIds[] = $attribute->getId();
             }
-            $product->setData($this->_usedProductAttributeIds, $usedProductAttributeIds);
+            $product->setData($this->usedProductAttributeIds, $usedProductAttributeIds);
         }
-        return $product->getData($this->_usedProductAttributeIds);
+        return $product->getData($this->usedProductAttributeIds);
     }
 
     /**
@@ -305,7 +335,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      */
     public function getUsedProductAttributes($product)
     {
-        if (!$product->hasData($this->_usedProductAttributes)) {
+        if (!$product->hasData($this->usedProductAttributes)) {
             $usedProductAttributes = [];
             $usedAttributes = [];
             foreach ($this->getConfigurableAttributes($product) as $attribute) {
@@ -316,9 +346,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
                 }
             }
             $product->setData($this->_usedAttributes, $usedAttributes);
-            $product->setData($this->_usedProductAttributes, $usedProductAttributes);
+            $product->setData($this->usedProductAttributes, $usedProductAttributes);
         }
-        return $product->getData($this->_usedProductAttributes);
+        return $product->getData($this->usedProductAttributes);
     }
 
     /**
@@ -529,57 +559,92 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      * @param \Magento\Catalog\Model\Product $product
      * @return $this
      * @throws \InvalidArgumentException
+     * @deprecated the \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute should be used instead
      */
     public function save($product)
     {
         parent::save($product);
 
-        /* Save attributes information */
+        $extensionAttributes = $product->getExtensionAttributes();
+
+        // this approach is needed for 3rd-party extensions which are not using extension attributes
+        if (empty($extensionAttributes->getConfigurableProductOptions())) {
+            $this->saveConfigurableOptions($product);
+        }
+
+        if (empty($extensionAttributes->getConfigurableProductLinks())) {
+            $this->saveRelatedProducts($product);
+        }
+        return $this;
+    }
+
+    /**
+     * Save configurable product attributes
+     *
+     * @param ProductInterface $product
+     * @return void
+     * @throws \Exception
+     * @deprecated
+     */
+    private function saveConfigurableOptions(ProductInterface $product)
+    {
         $data = $product->getConfigurableAttributesData();
-        if ($data) {
-            foreach ($data as $attributeData) {
-                /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */
-                $configurableAttribute = $this->_configurableAttributeFactory->create();
-                if (!$product->getIsDuplicate()) {
-                    if (!empty($attributeData['id'])) {
-                        $configurableAttribute->load($attributeData['id']);
-                        $attributeData['attribute_id'] = $configurableAttribute->getAttributeId();
-                    } elseif (!empty($attributeData['attribute_id'])) {
-                        $attribute = $this->_eavConfig->getAttribute(
-                            \Magento\Catalog\Model\Product::ENTITY, $attributeData['attribute_id']
+        if (!$data) {
+            return;
+        }
+
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
+        foreach ($data as $attributeData) {
+            /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */
+            $configurableAttribute = $this->configurableAttributeFactory->create();
+            if (!$product->getIsDuplicate()) {
+                if (!empty($attributeData['id'])) {
+                    $configurableAttribute->load($attributeData['id']);
+                    $attributeData['attribute_id'] = $configurableAttribute->getAttributeId();
+                } elseif (!empty($attributeData['attribute_id'])) {
+                    $attribute = $this->_eavConfig->getAttribute(
+                        \Magento\Catalog\Model\Product::ENTITY, $attributeData['attribute_id']
+                    );
+                    $attributeData['attribute_id'] = $attribute->getId();
+                    if (!$this->canUseAttribute($attribute)) {
+                        throw new \InvalidArgumentException(
+                            'Provided attribute can not be used with configurable product'
                         );
-                        $attributeData['attribute_id'] = $attribute->getId();
-                        if (!$this->canUseAttribute($attribute)) {
-                            throw new \InvalidArgumentException(
-                                'Provided attribute can not be used with configurable product'
-                            );
-                        }
-                        $configurableAttribute->loadByProductAndAttribute($product, $attribute);
                     }
+                    $configurableAttribute->loadByProductAndAttribute($product, $attribute);
                 }
-                unset($attributeData['id']);
-                $configurableAttribute
-                    ->addData($attributeData)
-                    ->setStoreId($product->getStoreId())
-                    ->setProductId($product->getId())
-                    ->save();
             }
-            /** @var $configurableAttributesCollection \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection  */
-            $configurableAttributesCollection = $this->_attributeCollectionFactory->create();
-            $configurableAttributesCollection->setProductFilter($product);
-            $configurableAttributesCollection->addFieldToFilter(
-                'attribute_id',
-                ['nin' => $this->getUsedProductAttributeIds($product)]
-            );
-            $configurableAttributesCollection->walk('delete');
+            unset($attributeData['id']);
+            $configurableAttribute
+                ->addData($attributeData)
+                ->setStoreId($product->getStoreId())
+                ->setProductId($product->getData($metadata->getLinkField()))
+                ->save();
         }
+        /** @var $configurableAttributesCollection \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection  */
+        $configurableAttributesCollection = $this->_attributeCollectionFactory->create();
+        $configurableAttributesCollection->setProductFilter($product);
+        $configurableAttributesCollection->addFieldToFilter(
+            'attribute_id',
+            ['nin' => $this->getUsedProductAttributeIds($product)]
+        );
+        $configurableAttributesCollection->walk('delete');
+    }
 
-        /* Save product relations */
+    /**
+     * Save related products
+     *
+     * @param ProductInterface $product
+     * @return void
+     * @deprecated
+     */
+    private function saveRelatedProducts(ProductInterface $product)
+    {
         $productIds = $product->getAssociatedProductIds();
         if (is_array($productIds)) {
-            $this->_typeConfigurableFactory->create()->saveProducts($product, $productIds);
+            $this->typeConfigurableFactory->create()->saveProducts($product, $productIds);
         }
-        return $this;
     }
 
     /**
@@ -639,9 +704,11 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
             foreach ($attributesInfo as $attributeId => $attributeValue) {
                 $productCollection->addAttributeToFilter($attributeId, $attributeValue);
             }
+            /** @var \Magento\Catalog\Model\Product $productObject */
             $productObject = $productCollection->getFirstItem();
-            if ($productObject->getId()) {
-                return $this->productRepository->getById($productObject->getId());
+            $productLinkFieldId = $productObject->getId();
+            if ($productLinkFieldId) {
+                return $this->productRepository->getById($productLinkFieldId);
             }
 
             foreach ($this->getUsedProducts($product) as $productObject) {
@@ -749,9 +816,10 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
                 }
 
                 if ($subProduct) {
+                    $subProductLinkFieldId = $subProduct->getId();
                     $product->addCustomOption('attributes', serialize($attributes));
-                    $product->addCustomOption('product_qty_' . $subProduct->getId(), 1, $subProduct);
-                    $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct);
+                    $product->addCustomOption('product_qty_' . $subProductLinkFieldId, 1, $subProduct);
+                    $product->addCustomOption('simple_product', $subProductLinkFieldId, $subProduct);
 
                     $_result = $subProduct->getTypeInstance()->processConfiguration(
                         $buyRequest,
@@ -779,12 +847,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
                         }
                     }
 
-                    $_result[0]->setParentProductId(
-                        $product->getId()
-                    )->addCustomOption(
-                        'parent_product_id',
-                        $product->getId()
-                    );
+                    $productLinkFieldId = $product->getId();
+                    $_result[0]->setParentProductId($productLinkFieldId)
+                        ->addCustomOption('parent_product_id', $productLinkFieldId);
                     if ($this->_isStrictProcessMode($processMode)) {
                         $_result[0]->setCartQty(1);
                     }
@@ -1016,9 +1081,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      */
     public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
     {
-        $this->_typeConfigurableFactory->create()->saveProducts($product, []);
+        $this->typeConfigurableFactory->create()->saveProducts($product, []);
         /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */
-        $configurableAttribute = $this->_configurableAttributeFactory->create();
+        $configurableAttribute = $this->configurableAttributeFactory->create();
         $configurableAttribute->deleteByProduct($product);
     }
 
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
index 29b866880c2906fbe25d49b284251bd7454b18e8..46579e63d12c104e662cf4da26afb3429906325b 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -7,6 +7,9 @@
  */
 namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 
+use Magento\Framework\Api\AttributeValueFactory;
+use Magento\Framework\Model\Entity\MetadataPool;
+use Magento\Catalog\Api\Data\ProductInterface;
 
 /**
  * @method Attribute _getResource()
@@ -28,6 +31,43 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
     const KEY_PRODUCT_ID = 'product_id';
     /**#@-*/
 
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * @param \Magento\Framework\Model\Context $context
+     * @param \Magento\Framework\Registry $registry
+     * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
+     * @param AttributeValueFactory $customAttributeFactory
+     * @param MetadataPool $metadataPool
+     * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
+     * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
+     * @param array $data
+     */
+    public function __construct(
+        \Magento\Framework\Model\Context $context,
+        \Magento\Framework\Registry $registry,
+        \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
+        AttributeValueFactory $customAttributeFactory,
+        MetadataPool $metadataPool,
+        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
+        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
+        array $data = []
+    ) {
+        $this->metadataPool = $metadataPool;
+        parent::__construct(
+            $context,
+            $registry,
+            $extensionFactory,
+            $customAttributeFactory,
+            $resource,
+            $resourceCollection,
+            $data
+        );
+    }
+
     /**
      * Initialize resource model
      *
@@ -47,6 +87,7 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
     {
         return $this->getData('options');
     }
+
     /**
      * {@inheritdoc}
      */
@@ -82,7 +123,12 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
      */
     public function loadByProductAndAttribute($product, $attribute)
     {
-        $id = $this->_getResource()->getIdByProductIdAndAttributeId($this, $product->getId(), $attribute->getId());
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+        $id = $this->_getResource()->getIdByProductIdAndAttributeId(
+            $this,
+            $product->getData($metadata->getLinkField()),
+            $attribute->getId()
+        );
         if ($id) {
             $this->load($id);
         }
@@ -96,7 +142,8 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
      */
     public function deleteByProduct($product)
     {
-        $this->_getResource()->deleteAttributesByProductId($product->getId());
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+        $this->_getResource()->deleteAttributesByProductId($product->getData($metadata->getLinkField()));
     }
 
     /**
@@ -136,6 +183,7 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
     }
 
     //@codeCoverageIgnoreStart
+
     /**
      * @param string $attributeId
      * @return $this
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
index 1bab6a2fb8d299a9d566151c19c63e4580353686..82fb0c2f227bfb93a1f7ca4a68501fd0d2ee87a6 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
@@ -5,8 +5,8 @@
  */
 namespace Magento\ConfigurableProduct\Model\Product;
 
-use Magento\Framework\Exception\LocalizedException;
 use Magento\Catalog\Model\Product\Type as ProductType;
+use Magento\Framework\Exception\LocalizedException;
 
 /**
  * Variation Handler
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
index 1df170963270445015e77f43fa01add88f24feec..4a2233b76a891ab4b9593258c7ef60bce00da763 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
@@ -25,6 +25,7 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
      */
     protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false)
     {
+        $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
         $connection = $this->getConnection();
         $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable();
         $select = $connection->select()->from(['e' => $this->getTable('catalog_product_entity')], ['entity_id']);
@@ -42,7 +43,7 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
             []
         )->joinLeft(
             ['l' => $this->getTable('catalog_product_super_link')],
-            'l.parent_id = e.entity_id',
+            'l.parent_id = e.' . $metadata->getLinkField(),
             []
         )->join(
             ['le' => $this->getTable('catalog_product_entity')],
@@ -62,7 +63,6 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
         )->group(
             ['e.entity_id', 'cw.website_id', 'cis.stock_id']
         );
-        $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
         $psExpr = $this->_addAttributeToSelect($select, 'status', 'e.' . $metadata->getLinkField(), 'cs.store_id');
         $psCond = $connection->quoteInto($psExpr . '=?', ProductStatus::STATUS_ENABLED);
 
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php
index 84395f7f5b138fd8ff1feb7832a6adf14994c0e2..97b2a0bbb75290dd3ea9dd87d569cbf14dabba01 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php
@@ -7,6 +7,8 @@
  */
 namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price;
 
+use Magento\Catalog\Api\Data\ProductInterface;
+
 class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice
 {
     /**
@@ -69,23 +71,40 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
      */
     private function getRelatedProducts($entityIds)
     {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         $select = $this->getConnection()->select()->union(
             [
                 $this->getConnection()->select()
-                    ->from($this->getTable('catalog_product_super_link'), 'parent_id')
-                    ->where('parent_id in (?)', $entityIds),
+                    ->from(
+                        ['e' => $this->getTable('catalog_product_entity')],
+                        'e.entity_id'
+                    )->join(
+                        ['cpsl' => $this->getTable('catalog_product_super_link')],
+                        'cpsl.parent_id = e.' . $metadata->getLinkField(),
+                        []
+                    )->where(
+                        'e.entity_id IN (?)',
+                        $entityIds
+                    ),
                 $this->getConnection()->select()
-                    ->from($this->getTable('catalog_product_super_link'), 'product_id')
-                    ->where('parent_id in (?)', $entityIds),
+                    ->from(
+                        ['cpsl' => $this->getTable('catalog_product_super_link')],
+                        'cpsl.product_id'
+                    )->join(
+                        ['e' => $this->getTable('catalog_product_entity')],
+                        'cpsl.parent_id = e.' . $metadata->getLinkField(),
+                        []
+                    )->where(
+                        'e.entity_id IN (?)',
+                        $entityIds
+                    ),
                 $this->getConnection()->select()
                     ->from($this->getTable('catalog_product_super_link'), 'product_id')
                     ->where('product_id in (?)', $entityIds),
             ]
         );
-        return array_map(
-            'intval',
-            $this->getConnection()->fetchCol($select)
-        );
+
+        return array_map('intval', $this->getConnection()->fetchCol($select));
     }
 
     /**
@@ -139,6 +158,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
      */
     protected function _applyConfigurableOption()
     {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         $connection = $this->getConnection();
         $coaTable = $this->_getConfigurableOptionAggregateTable();
         $copTable = $this->_getConfigurableOptionPriceTable();
@@ -149,10 +169,14 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
         $select = $connection->select()->from(
             ['i' => $this->_getDefaultFinalPriceTable()],
             []
+        )->join(
+            ['e' => $this->getTable('catalog_product_entity')],
+            'e.entity_id = i.entity_id',
+            ['parent_id' => 'e.entity_id']
         )->join(
             ['l' => $this->getTable('catalog_product_super_link')],
-            'l.parent_id = i.entity_id',
-            ['parent_id', 'product_id']
+            'l.parent_id = e.' . $metadata->getLinkField(),
+            ['product_id']
         )->columns(
             ['customer_group_id', 'website_id'],
             'i'
@@ -163,7 +187,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
         )->where(
             'le.required_options=0'
         )->group(
-            ['l.parent_id', 'i.customer_group_id', 'i.website_id', 'l.product_id']
+            ['parent_id', 'i.customer_group_id', 'i.website_id', 'l.product_id']
         );
         $priceColumn = $this->_addAttributeToSelect($select, 'price', 'l.product_id', 0, null, true);
         $tierPriceColumn = $connection->getCheckSql("MIN(i.tier_price) IS NOT NULL", "i.tier_price", 'NULL');
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php
index 8dda060c4f75ff4428bb4ebede6985265c61def2..5d7d9acce0c9b37aac3a5895a3122ab8c1654ce8 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php
@@ -8,6 +8,7 @@
 namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type;
 
 use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
 use Magento\Framework\Model\Entity\MetadataPool;
 
 class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
@@ -17,12 +18,12 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      *
      * @var \Magento\Catalog\Model\ResourceModel\Product\Relation
      */
-    protected $_catalogProductRelation;
+    protected $catalogProductRelation;
 
     /**
      * @var MetadataPool
      */
-    protected $metadataPool;
+    private $metadataPool;
 
     /**
      * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
@@ -36,7 +37,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         MetadataPool $metadataPool,
         $connectionName = null
     ) {
-        $this->_catalogProductRelation = $catalogProductRelation;
+        $this->catalogProductRelation = $catalogProductRelation;
         $this->metadataPool = $metadataPool;
         parent::__construct($context, $connectionName);
     }
@@ -51,6 +52,27 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         $this->_init('catalog_product_super_link', 'link_id');
     }
 
+    /**
+     * Get product entity id by product attribute
+     *
+     * @param OptionInterface $option
+     * @return int
+     */
+    public function getEntityIdByAttribute(OptionInterface $option)
+    {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
+        $select = $this->getConnection()->select()->from(
+            ['e' => $this->getTable('catalog_product_entity')],
+            ['e.entity_id']
+        )->where(
+            'e.' . $metadata->getLinkField() . '=?',
+            $option->getProductId()
+        )->limit(1);
+
+        return (int) $this->getConnection()->fetchOne($select);
+    }
+
     /**
      * Save configurable product relations
      *
@@ -58,39 +80,37 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      * @param array $productIds the children id array
      * @return $this
      */
-    public function saveProducts($mainProduct, $productIds)
+    public function saveProducts($mainProduct, array $productIds)
     {
-        $isProductInstance = false;
-        if ($mainProduct instanceof \Magento\Catalog\Model\Product) {
-            $mainProductId = $mainProduct->getId();
-            $isProductInstance = true;
-        }
-        $old = [];
-        if (!$mainProduct->getIsDuplicate()) {
-            $old = $mainProduct->getTypeInstance()->getUsedProductIds($mainProduct);
+        if (!$mainProduct instanceof ProductInterface) {
+            return $this;
         }
 
-        $insert = array_diff($productIds, $old);
-        $delete = array_diff($old, $productIds);
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+        $productId = $mainProduct->getData($metadata->getLinkField());
 
-        if ((!empty($insert) || !empty($delete)) && $isProductInstance) {
-            $mainProduct->setIsRelationsChanged(true);
+        $data = [];
+        foreach ($productIds as $id) {
+            $data[] = ['product_id' => (int) $id, 'parent_id' => (int) $productId];
         }
 
-        if (!empty($delete)) {
-            $where = ['parent_id = ?' => $mainProductId, 'product_id IN(?)' => $delete];
-            $this->getConnection()->delete($this->getMainTable(), $where);
+        if (!empty($data)) {
+            $this->getConnection()->insertOnDuplicate(
+                $this->getMainTable(),
+                $data,
+                ['product_id', 'parent_id']
+            );
         }
-        if (!empty($insert)) {
-            $data = [];
-            foreach ($insert as $childId) {
-                $data[] = ['product_id' => (int)$childId, 'parent_id' => (int)$mainProductId];
-            }
-            $this->getConnection()->insertMultiple($this->getMainTable(), $data);
+
+        $where = ['parent_id = ?' => $productId];
+        if (!empty($productIds)) {
+            $where['product_id NOT IN(?)'] = $productIds;
         }
-        $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
+
+        $this->getConnection()->delete($this->getMainTable(), $where);
+
         // configurable product relations should be added to relation table
-        $this->_catalogProductRelation->processRelations($mainProduct->getData($linkField), $productIds);
+        $this->catalogProductRelation->processRelations($productId, $productIds);
 
         return $this;
     }
@@ -108,15 +128,16 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      */
     public function getChildrenIds($parentId, $required = true)
     {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         $select = $this->getConnection()->select()->from(
-            ['l' => $this->getMainTable()],
-            ['product_id', 'parent_id']
-        )->join(
             ['e' => $this->getTable('catalog_product_entity')],
-            'e.entity_id = l.product_id AND e.required_options = 0',
+            ['l.product_id']
+        )->join(
+            ['l' => $this->getMainTable()],
+            'l.parent_id = e.' . $metadata->getLinkField(),
             []
         )->where(
-            'parent_id IN (?)',
+            'e.entity_id IN (?) AND e.required_options = 0',
             $parentId
         );
 
@@ -129,7 +150,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     }
 
     /**
-     * Retrieve parent ids array by requered child
+     * Retrieve parent ids array by required child
      *
      * @param int|array $childId
      * @return string[]
@@ -138,15 +159,19 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     {
         $parentIds = [];
 
-        $select = $this->getConnection()->select()->from(
-            $this->getMainTable(),
-            ['product_id', 'parent_id']
-        )->where(
-            'product_id IN(?)',
-            $childId
-        );
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
+        $select = $this->getConnection()
+            ->select()
+            ->from(['l' => $this->getMainTable()], [])
+            ->join(
+                ['e' => $this->getTable('catalog_product_entity')],
+                'e.' . $metadata->getLinkField() . ' = l.parent_id',
+                ['e.entity_id']
+            )->where('l.product_id IN(?)', $childId);
+
         foreach ($this->getConnection()->fetchAll($select) as $row) {
-            $parentIds[] = $row['parent_id'];
+            $parentIds[] = $row['entity_id'];
         }
 
         return $parentIds;
@@ -162,16 +187,22 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     public function getConfigurableOptions($product, $attributes)
     {
         $attributesOptionsData = [];
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+        $productLinkFieldId = $product->getData($metadata->getLinkField());
         foreach ($attributes as $superAttribute) {
             $select = $this->getConnection()->select()->from(
                 ['super_attribute' => $this->getTable('catalog_product_super_attribute')],
                 [
                     'sku' => 'entity.sku',
-                    'product_id' => 'super_attribute.product_id',
+                    'product_id' => 'product_entity.entity_id',
                     'attribute_code' => 'attribute.attribute_code',
                     'option_title' => 'option_value.value',
                     'super_attribute_label' => 'attribute_label.value',
                 ]
+            )->joinInner(
+                ['product_entity' => $this->getTable('catalog_product_entity')],
+                'product_entity.' . $metadata->getLinkField() . ' = super_attribute.product_id',
+                []
             )->joinInner(
                 ['product_link' => $this->getTable('catalog_product_super_link')],
                 'product_link.parent_id = super_attribute.product_id',
@@ -217,7 +248,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
                 []
             )->where(
                 'super_attribute.product_id = ?',
-                $product->getId()
+                $productLinkFieldId
             );
 
             $attributesOptionsData[$superAttribute->getAttributeId()] = $this->getConnection()->fetchAll($select);
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
index dbca1777f450b4c810baa4bc2181fbee1665ac96..0a11e18b822e0007eefed9205f6f04b27adb70dd 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
@@ -8,6 +8,8 @@
 namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute;
 
 use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use Magento\Framework\Model\Entity\MetadataPool;
+use Magento\Catalog\Api\Data\ProductInterface;
 
 /**
  * @SuppressWarnings(PHPMD.LongVariable)
@@ -50,6 +52,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      */
     protected $_storeManager;
 
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
     /**
      * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
      * @param \Psr\Log\LoggerInterface $logger
@@ -59,6 +66,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable
      * @param \Magento\Catalog\Helper\Data $catalogData
      * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource
+     * @param MetadataPool $metadataPool
      * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
@@ -71,11 +79,13 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
         \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable,
         \Magento\Catalog\Helper\Data $catalogData,
         \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource,
+        MetadataPool $metadataPool,
         \Magento\Framework\DB\Adapter\AdapterInterface $connection = null
     ) {
         $this->_storeManager = $storeManager;
         $this->_productTypeConfigurable = $catalogProductTypeConfigurable;
         $this->_catalogData = $catalogData;
+        $this->metadataPool = $metadataPool;
         parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
     }
 
@@ -101,8 +111,9 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      */
     public function setProductFilter($product)
     {
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
         $this->_product = $product;
-        return $this->addFieldToFilter('product_id', $product->getId());
+        return $this->addFieldToFilter('product_id', $product->getData($metadata->getLinkField()));
     }
 
     /**
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php
index 7b73e1eabc5debe5e23f072378c9d139075cdf84..a88f34db5fe07dad06bb2503807ae27d9c9289ce 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php
@@ -7,6 +7,15 @@
  */
 namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product;
 
+use Magento\Customer\Api\GroupManagementInterface;
+use Magento\Framework\Model\Entity\MetadataPool;
+use Magento\Catalog\Api\Data\ProductInterface;
+
+/**
+ * Class Collection
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
 {
     /**
@@ -16,6 +25,87 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      */
     protected $_linkTable;
 
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
+     * @param \Psr\Log\LoggerInterface $logger
+     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
+     * @param \Magento\Framework\Event\ManagerInterface $eventManager
+     * @param \Magento\Eav\Model\Config $eavConfig
+     * @param \Magento\Framework\App\ResourceConnection $resource
+     * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory
+     * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper
+     * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param \Magento\Framework\Module\Manager $moduleManager
+     * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
+     * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory
+     * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl
+     * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
+     * @param \Magento\Customer\Model\Session $customerSession
+     * @param \Magento\Framework\Stdlib\DateTime $dateTime
+     * @param GroupManagementInterface $groupManagement
+     * @param \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation
+     * @param MetadataPool $metadataPool
+     * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
+     *
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+     */
+    public function __construct(
+        \Magento\Framework\Data\Collection\EntityFactory $entityFactory,
+        \Psr\Log\LoggerInterface $logger,
+        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
+        \Magento\Framework\Event\ManagerInterface $eventManager,
+        \Magento\Eav\Model\Config $eavConfig,
+        \Magento\Framework\App\ResourceConnection $resource,
+        \Magento\Eav\Model\EntityFactory $eavEntityFactory,
+        \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper,
+        \Magento\Framework\Validator\UniversalFactory $universalFactory,
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
+        \Magento\Framework\Module\Manager $moduleManager,
+        \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState,
+        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
+        \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory,
+        \Magento\Catalog\Model\ResourceModel\Url $catalogUrl,
+        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
+        \Magento\Customer\Model\Session $customerSession,
+        \Magento\Framework\Stdlib\DateTime $dateTime,
+        GroupManagementInterface $groupManagement,
+        \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation,
+        MetadataPool $metadataPool,
+        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null
+    ) {
+        $this->metadataPool = $metadataPool;
+        parent::__construct(
+            $entityFactory,
+            $logger,
+            $fetchStrategy,
+            $eventManager,
+            $eavConfig,
+            $resource,
+            $eavEntityFactory,
+            $resourceHelper,
+            $universalFactory,
+            $storeManager,
+            $moduleManager,
+            $catalogProductFlatState,
+            $scopeConfig,
+            $productOptionFactory,
+            $catalogUrl,
+            $localeDate,
+            $customerSession,
+            $dateTime,
+            $groupManagement,
+            $productLimitation,
+            $connection
+        );
+    }
+
     /**
      * Assign link table name
      *
@@ -34,6 +124,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
     protected function _initSelect()
     {
         parent::_initSelect();
+
         $this->getSelect()->join(
             ['link_table' => $this->_linkTable],
             'link_table.product_id = e.entity_id',
@@ -51,7 +142,9 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
      */
     public function setProductFilter($product)
     {
-        $this->getSelect()->where('link_table.parent_id = ?', (int)$product->getId());
+        $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+
+        $this->getSelect()->where('link_table.parent_id = ?', $product->getData($metadata->getLinkField()));
         return $this;
     }
 
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
index 7537e2b7fcb2a4e33453f0a596ee020996e5cb61..b30b7d24e8f19f3000ab9f55dda252775e79ee0a 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
@@ -158,15 +158,6 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         )->will(
             $this->returnSelf()
         );
-        $this->configurableTypeMock->expects(
-            $this->once()
-        )->method(
-            'setUsedProductAttributeIds'
-        )->with(
-            ['attributes']
-        )->will(
-            $this->returnSelf()
-        );
         $this->productMock->expects(
             $this->once()
         )->method(
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php
index 5f71035b2a93609572514060626535915ff2ddb8..96c648bb4df989878eebdefd98a31042c97a8c48 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php
@@ -5,176 +5,254 @@
  */
 namespace Magento\ConfigurableProduct\Test\Unit\Controller\Adminhtml\Product\Initialization\Helper\Plugin;
 
-use \Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable;
+use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper;
+use Magento\Catalog\Model\Product;
+use Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable;
+use Magento\ConfigurableProduct\Helper\Product\Options\Factory;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableProduct;
+use Magento\ConfigurableProduct\Model\Product\VariationHandler;
+use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes;
+use Magento\Framework\App\Request\Http;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
+/**
+ * Class ConfigurableTest
+ */
 class ConfigurableTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable
+     * @var VariationHandler|MockObject
      */
-    protected $plugin;
+    private $variationHandler;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Http|MockObject
      */
-    protected $productTypeMock;
+    private $request;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Factory|MockObject
      */
-    protected $requestMock;
+    private $optionFactory;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Product|MockObject
      */
-    protected $productMock;
+    private $product;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Helper|MockObject
      */
-    protected $variationHandler;
+    private $subject;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Configurable
      */
-    protected $subjectMock;
+    private $plugin;
 
+    /**
+     * @inheritdoc
+     */
     protected function setUp()
     {
-        $this->productTypeMock = $this->getMock(
-            'Magento\ConfigurableProduct\Model\Product\Type\Configurable',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->variationHandler = $this->getMock(
-            'Magento\ConfigurableProduct\Model\Product\VariationHandler',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->requestMock = $this->getMock('\Magento\Framework\App\Request\Http', [], [], '', false);
-        $methods = [
-            'setNewVariationsAttributeSetId',
-            'setAssociatedProductIds',
-            'setCanSaveConfigurableAttributes',
-            'getTypeId',
-            '__wakeup',
-        ];
-        $this->productMock = $this->getMock('Magento\Catalog\Model\Product', $methods, [], '', false);
-        $this->subjectMock = $this->getMock(
-            'Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper',
-            [],
-            [],
-            '',
-            false
+        $this->variationHandler = $this->getMockBuilder(VariationHandler::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['generateSimpleProducts'])
+            ->getMock();
+
+        $this->request = $this->getMockBuilder(Http::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getParam', 'getPost'])
+            ->getMock();
+
+        $this->optionFactory = $this->getMockBuilder(Factory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'getTypeId', 'setAttributeSetId', 'getExtensionAttributes', 'setNewVariationsAttributeSetId',
+                'setCanSaveConfigurableAttributes', 'setExtensionAttributes'
+            ])
+            ->getMock();
+
+        $this->subject = $this->getMockBuilder(Helper::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->plugin = new Configurable(
+            $this->variationHandler,
+            $this->request,
+            $this->optionFactory
         );
-        $this->plugin = new Configurable($this->variationHandler, $this->productTypeMock, $this->requestMock);
     }
 
-    public function testAfterInitializeIfAttributesNotEmptyAndActionNameNotGenerateVariations()
+    /**
+     * @covers Configurable::afterInitialize
+     */
+    public function testAfterInitializeWithAttributesAndVariations()
     {
-        $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE);
-        $associatedProductIds = ['key' => 'value'];
-        $generatedProductIds = ['key_one' => 'value_one'];
-        $expectedArray = ['key' => 'value', 'key_one' => 'value_one'];
-        $attributes = ['key' => 'value'];
-        $postValue = 'postValue';
-        $postValueMap = [
-            ['new-variations-attribute-set-id', null, $postValue],
-            ['associated_product_ids', [], $associatedProductIds],
-            ['affect_configurable_product_attributes', null, $postValue],
+        $attributes = [
+            ['attribute_id' => 90, 'values' => [
+                ['value_index' => 12], ['value_index' => 13]
+            ]]
         ];
-        $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($postValueMap));
-
+        $valueMap = [
+            ['new-variations-attribute-set-id', null, 24],
+            ['associated_product_ids', [], []],
+            ['product', [], ['configurable_attributes_data' => $attributes]],
+        ];
+        $simpleProductsIds = [1, 2, 3];
+        $simpleProducts = ['simple1', 'simple2', 'simple3'];
         $paramValueMap = [
-            ['variations-matrix', [], $postValue],
+            ['variations-matrix', [], $simpleProducts],
             ['attributes', null, $attributes],
         ];
-        $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap));
-        $this->productTypeMock->expects(
-            $this->once()
-        )->method(
-            'setUsedProductAttributeIds'
-        )->with(
-            $attributes,
-            $this->productMock
-        );
-        $this->productMock->expects($this->once())->method('setNewVariationsAttributeSetId')->with($postValue);
-        $this->variationHandler->expects(
-            $this->once()
-        )->method(
-            'generateSimpleProducts'
-        )->with(
-            $this->productMock,
-            $postValue
-        )->will(
-            $this->returnValue($generatedProductIds)
-        );
-        $this->productMock->expects($this->once())->method('setAssociatedProductIds')->with($expectedArray);
-        $this->productMock->expects($this->once())->method('setCanSaveConfigurableAttributes')->with(true);
-        $this->plugin->afterInitialize($this->subjectMock, $this->productMock);
+
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(ConfigurableProduct::TYPE_CODE);
+
+        $this->request->expects(static::any())
+            ->method('getPost')
+            ->willReturnMap($valueMap);
+
+        $this->request->expects(static::any())
+            ->method('getParam')
+            ->willReturnMap($paramValueMap);
+
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
+            ->getMockForAbstractClass();
+        $this->product->expects(static::once())
+            ->method('getExtensionAttributes')
+            ->willReturn($extensionAttributes);
+
+        $this->optionFactory->expects(static::once())
+            ->method('create')
+            ->with($attributes)
+            ->willReturn($attributes);
+
+        $extensionAttributes->expects(static::once())
+            ->method('setConfigurableProductOptions')
+            ->with($attributes);
+
+        $this->variationHandler->expects(static::once())
+            ->method('generateSimpleProducts')
+            ->with($this->product, $simpleProducts)
+            ->willReturn($simpleProductsIds);
+
+        $extensionAttributes->expects(static::once())
+            ->method('setConfigurableProductLinks')
+            ->with($simpleProductsIds);
+
+        $this->product->expects(static::once())
+            ->method('setExtensionAttributes')
+            ->with($extensionAttributes);
+
+        $this->plugin->afterInitialize($this->subject, $this->product);
     }
 
-    public function testAfterInitializeIfAttributesNotEmptyAndActionNameGenerateVariations()
+    /**
+     * @covers Configurable::afterInitialize
+     */
+    public function testAfterInitializeWithAttributesAndWithoutVariations()
     {
-        $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE);
-        $associatedProductIds = ['key' => 'value'];
-        $attributes = ['key' => 'value'];
-        $postValue = 'postValue';
+        $attributes = [
+            ['attribute_id' => 90, 'values' => [
+                ['value_index' => 12], ['value_index' => 13]
+            ]]
+        ];
         $valueMap = [
-            ['new-variations-attribute-set-id', null, $postValue],
-            ['associated_product_ids', [], $associatedProductIds],
-            ['affect_configurable_product_attributes', null, $postValue],
+            ['new-variations-attribute-set-id', null, 24],
+            ['associated_product_ids', [], []],
+            ['product', [], ['configurable_attributes_data' => $attributes]],
         ];
-        $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($valueMap));
         $paramValueMap = [
             ['variations-matrix', [], []],
             ['attributes', null, $attributes],
         ];
-        $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap));
-        $this->productTypeMock->expects(
-            $this->once()
-        )->method(
-            'setUsedProductAttributeIds'
-        )->with(
-            $attributes,
-            $this->productMock
-        );
-        $this->productMock->expects($this->once())->method('setNewVariationsAttributeSetId')->with($postValue);
-        $this->productTypeMock->expects($this->never())->method('generateSimpleProducts');
-        $this->productMock->expects($this->once())->method('setAssociatedProductIds')->with($associatedProductIds);
-        $this->productMock->expects($this->once())->method('setCanSaveConfigurableAttributes')->with(true);
-        $this->plugin->afterInitialize($this->subjectMock, $this->productMock);
+
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(ConfigurableProduct::TYPE_CODE);
+
+        $this->request->expects(static::any())
+            ->method('getPost')
+            ->willReturnMap($valueMap);
+
+        $this->request->expects(static::any())
+            ->method('getParam')
+            ->willReturnMap($paramValueMap);
+
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
+            ->getMockForAbstractClass();
+        $this->product->expects(static::once())
+            ->method('getExtensionAttributes')
+            ->willReturn($extensionAttributes);
+
+        $this->optionFactory->expects(static::once())
+            ->method('create')
+            ->with($attributes)
+            ->willReturn($attributes);
+
+        $extensionAttributes->expects(static::once())
+            ->method('setConfigurableProductOptions')
+            ->with($attributes);
+
+        $this->variationHandler->expects(static::never())
+            ->method('generateSimpleProducts');
+
+        $extensionAttributes->expects(static::once())
+            ->method('setConfigurableProductLinks');
+
+        $this->product->expects(static::once())
+            ->method('setExtensionAttributes')
+            ->with($extensionAttributes);
+
+        $this->plugin->afterInitialize($this->subject, $this->product);
     }
 
+    /**
+     * @covers Configurable::afterInitialize
+     */
     public function testAfterInitializeIfAttributesEmpty()
     {
-        $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE);
-        $this->requestMock->expects(
-            $this->once()
-        )->method(
-            'getParam'
-        )->with(
-            'attributes'
-        )->will(
-            $this->returnValue([])
-        );
-        $this->productTypeMock->expects($this->never())->method('setUsedProductAttributeIds');
-        $this->requestMock->expects($this->never())->method('getPost');
-        $this->productTypeMock->expects($this->never())->method('generateSimpleProducts');
-        $this->plugin->afterInitialize($this->subjectMock, $this->productMock);
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(ConfigurableProduct::TYPE_CODE);
+        $this->request->expects(static::once())
+            ->method('getParam')
+            ->with('attributes')
+            ->willReturn([]);
+        $this->product->expects(static::never())
+            ->method('getExtensionAttributes');
+        $this->request->expects(static::once())
+            ->method('getPost');
+        $this->variationHandler->expects(static::never())
+            ->method('generateSimpleProducts');
+        $this->plugin->afterInitialize($this->subject, $this->product);
     }
 
+    /**
+     * @covers Configurable::afterInitialize
+     */
     public function testAfterInitializeForNotConfigurableProduct()
     {
-        $this->productMock->expects($this->once())->method('getTypeId')->willReturn('non-configurable');
-        $this->productTypeMock->expects($this->never())->method('setUsedProductAttributeIds');
-        $this->requestMock->expects($this->never())->method('getPost');
-        $this->productTypeMock->expects($this->never())->method('generateSimpleProducts');
-        $this->plugin->afterInitialize($this->subjectMock, $this->productMock);
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn('non-configurable');
+        $this->product->expects(static::never())
+            ->method('getExtensionAttributes');
+        $this->request->expects(static::once())
+            ->method('getPost');
+        $this->variationHandler->expects(static::never())
+            ->method('generateSimpleProducts');
+        $this->plugin->afterInitialize($this->subject, $this->product);
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..499f992aab0af76a7628f768bf7d2b7c56b140b7
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Helper\Product\Options;
+
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterface;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory;
+use Magento\ConfigurableProduct\Helper\Product\Options\Factory;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class FactoryTest
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class FactoryTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Configurable|MockObject
+     */
+    private $configurable;
+
+    /**
+     * @var AttributeFactory|MockObject
+     */
+    private $attributeFactory;
+
+    /**
+     * @var OptionValueInterfaceFactory|MockObject
+     */
+    private $optionValueFactory;
+
+    /**
+     * @var ProductAttributeRepositoryInterface|MockObject
+     */
+    private $productAttributeRepository;
+
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var Factory
+     */
+    private $factory;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+
+        $this->configurable = $this->getMockBuilder(Configurable::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['canUseAttribute'])
+            ->getMock();
+
+        $this->attributeFactory = $this->getMockBuilder(AttributeFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->optionValueFactory = $this->getMockBuilder(OptionValueInterfaceFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->productAttributeRepository = $this->getMock(ProductAttributeRepositoryInterface::class);
+
+        $this->factory = new Factory(
+            $this->configurable,
+            $this->attributeFactory,
+            $this->optionValueFactory,
+            $this->productAttributeRepository
+        );
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Factory::create
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Provided attribute can not be used with configurable product.
+     */
+    public function testCreateWithException()
+    {
+        $attributeId = 90;
+        $data = [
+            ['attribute_id' => $attributeId, 'values' => [
+                ['value_index' => 12], ['value_index' => 13]
+            ]]
+        ];
+
+        $attribute = $this->getMockBuilder(Attribute::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setValues', 'getData'])
+            ->getMock();
+
+        $this->attributeFactory->expects(static::once())
+            ->method('create')
+            ->willReturn($attribute);
+
+        $eavAttribute = $this->getMockBuilder(EavAttribute::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productAttributeRepository->expects(static::once())
+            ->method('get')
+            ->with($attributeId)
+            ->willReturn($eavAttribute);
+
+        $this->configurable->expects(static::once())
+            ->method('canUseAttribute')
+            ->with($eavAttribute)
+            ->willReturn(false);
+
+        $this->factory->create($data);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Factory::create
+     */
+    public function testCreate()
+    {
+        $attributeId = 90;
+        $valueIndex = 12;
+        $item = ['attribute_id' => $attributeId, 'values' => [['value_index' => $valueIndex]]];
+        $data = [$item];
+
+        $attribute = $this->getMockBuilder(Attribute::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setValues', 'setData', '__wakeup'])
+            ->getMock();
+
+        $this->attributeFactory->expects(static::once())
+            ->method('create')
+            ->willReturn($attribute);
+
+        $eavAttribute = $this->getMockBuilder(EavAttribute::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productAttributeRepository->expects(static::once())
+            ->method('get')
+            ->with($attributeId)
+            ->willReturn($eavAttribute);
+
+        $this->configurable->expects(static::once())
+            ->method('canUseAttribute')
+            ->with($eavAttribute)
+            ->willReturn(true);
+
+        $option = $this->getMock(OptionValueInterface::class);
+        $option->expects(static::once())
+            ->method('setValueIndex')
+            ->with($valueIndex)
+            ->willReturnSelf();
+        $this->optionValueFactory->expects(static::once())
+            ->method('create')
+            ->willReturn($option);
+
+        $attribute->expects(static::once())
+            ->method('setData')
+            ->with($item);
+
+        $attribute->expects(static::once())
+            ->method('setValues')
+            ->with([$option]);
+
+        $result = $this->factory->create($data);
+        static::assertSame([$attribute], $result);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b0f840ac224c24d600e328aa459213e847ee5365
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Helper\Product\Options;
+
+use Magento\Catalog\Model\Product;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterface;
+use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory;
+use Magento\ConfigurableProduct\Helper\Product\Options\Loader;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class LoaderTest
+ */
+class LoaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var OptionValueInterfaceFactory|MockObject
+     */
+    private $optionValueFactory;
+
+    /**
+     * @var Product|MockObject
+     */
+    private $product;
+
+    /**
+     * @var Configurable|MockObject
+     */
+    private $configurable;
+
+    /**
+     * @var Loader
+     */
+    private $loader;
+
+    protected function setUp()
+    {
+        $this->optionValueFactory = $this->getMockBuilder(OptionValueInterfaceFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeInstance'])
+            ->getMock();
+
+        $this->configurable = $this->getMockBuilder(Configurable::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getConfigurableAttributes'])
+            ->getMock();
+
+        $this->loader = new Loader($this->optionValueFactory);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Loader::load
+     */
+    public function testLoad()
+    {
+        $option = [
+            'value_index' => 23
+        ];
+
+        $this->product->expects(static::once())
+            ->method('getTypeInstance')
+            ->willReturn($this->configurable);
+
+        $attribute = $this->getMockBuilder(Attribute::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getOptions', 'setValues'])
+            ->getMock();
+        $attributes = [$attribute];
+
+        $this->configurable->expects(static::once())
+            ->method('getConfigurableAttributes')
+            ->with($this->product)
+            ->willReturn($attributes);
+
+        $attribute->expects(static::once())
+            ->method('getOptions')
+            ->willReturn([$option]);
+
+        $optionValue = $this->getMockForAbstractClass(OptionValueInterface::class);
+        $this->optionValueFactory->expects(static::once())
+            ->method('create')
+            ->willReturn($optionValue);
+        $optionValue->expects(static::once())
+            ->method('setValueIndex')
+            ->with($option['value_index']);
+
+        $attribute->expects(static::once())
+            ->method('setValues')
+            ->with([$optionValue]);
+
+        $options = $this->loader->load($this->product);
+        static::assertSame([$attribute], $options);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php
index 71cac64a95d3782ad845d41bc00c3337bef42638..c5b30adcb606b49d8364b60b7963027d069568ce 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php
@@ -5,7 +5,10 @@
  */
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Attribute;
 
+use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Model\Entity\EntityMetadata;
+use Magento\Framework\Model\Entity\MetadataPool;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class LockValidatorTest extends \PHPUnit_Framework_TestCase
@@ -30,6 +33,11 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase
      */
     private $select;
 
+    /**
+     * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $metadataPoolMock;
+
     protected function setUp()
     {
         $helper = new ObjectManager($this);
@@ -48,9 +56,21 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
+        $this->metadataPoolMock = $this->getMockBuilder(MetadataPool::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->metadataPoolMock->expects(self::once())
+            ->method('getMetadata')
+            ->with(ProductInterface::class)
+            ->willReturn($this->getMetaDataMock());
+
         $this->model = $helper->getObject(
             'Magento\ConfigurableProduct\Model\Attribute\LockValidator',
-            ['resource' => $this->resource]
+            [
+                'resource' => $this->resource,
+                'metadataPool' => $this->metadataPoolMock
+            ]
         );
     }
 
@@ -59,6 +79,22 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase
         $this->validate(false);
     }
 
+    /**
+     * @return EntityMetadata|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private function getMetaDataMock()
+    {
+        $metadata = $this->getMockBuilder(EntityMetadata::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $metadata->expects(self::once())
+            ->method('getLinkField')
+            ->willReturn('entity_id');
+
+        return $metadata;
+    }
+
     /**
      * @expectedException \Magento\Framework\Exception\LocalizedException
      * @expectedExceptionMessage This attribute is used in configurable products.
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
index db131d2bb5cb6ca1519c97447c3840304b85093b..01d1f6adc386e1139e1454461ab50af1acaba6f0 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
@@ -3,11 +3,17 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\ConfigurableProduct\Test\Unit\Model;
 
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\ConfigurableProduct\Helper\Product\Options\Loader;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
 
+/**
+ * Class OptionRepositoryTest
+ */
 class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -35,20 +41,32 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $optionResource;
 
+    /**
+     * @var Loader|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $optionLoader;
+
     protected function setUp()
     {
-        $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface');
+        $this->productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class)
+            ->getMockForAbstractClass();
+
         $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+
         $this->configurableTypeResource = $this->getMockBuilder(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable'
-        )
-            ->disableOriginalConstructor()
+        )->disableOriginalConstructor()
             ->getMock();
+
         $this->optionResource = $this->getMockBuilder(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute'
-        )
+        )->disableOriginalConstructor()
+            ->getMock();
+
+        $this->optionLoader = $this->getMockBuilder(Loader::class)
             ->disableOriginalConstructor()
             ->getMock();
+
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
         $this->model = $objectManager->getObject(
             '\Magento\ConfigurableProduct\Model\OptionRepository',
@@ -56,38 +74,39 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
                 'productRepository' => $this->productRepositoryMock,
                 'configurableTypeResource' => $this->configurableTypeResource,
                 'optionResource' => $this->optionResource,
+                'optionLoader' => $this->optionLoader
             ]
         );
     }
 
     public function testGet()
     {
-        $productSku = "configurable";
         $optionId = 3;
+        $productSku = "configurable";
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->productMock->expects(self::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
+
+        $this->productRepositoryMock->expects(self::once())
             ->method('get')
             ->with($productSku)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn(Configurable::TYPE_CODE);
 
-        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
-        $optionMock->expects($this->once())
+        $optionMock = $this->getMock(OptionInterface::class);
+        $optionMock->expects(self::once())
             ->method('getId')
             ->willReturn($optionId);
-        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
-            ->setMethods(['getConfigurableProductOptions'])
-            ->getMock();
-        $productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductOptions')
+
+        $this->optionLoader->expects(self::once())
+            ->method('load')
+            ->with($this->productMock)
             ->willReturn([$optionMock]);
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($productExtensionMock);
 
-        $this->assertEquals($optionMock, $this->model->get($productSku, $optionId));
+        self::assertEquals(
+            $optionMock,
+            $this->model->get($productSku, $optionId)
+        );
     }
 
     /**
@@ -99,13 +118,21 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
         $productSku = "configurable";
         $optionId = 3;
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->productMock->expects(self::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE . '-not');
+
+        $this->productRepositoryMock->expects(self::once())
             ->method('get')
             ->with($productSku)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn('simple');
+
+        $optionMock = $this->getMock(OptionInterface::class);
+        $optionMock->expects(self::never())
+            ->method('getId');
+
+        $this->optionLoader->expects(self::never())
+            ->method('load');
 
         $this->model->get($productSku, $optionId);
     }
@@ -116,25 +143,25 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetNotProductById()
     {
-        $productId = 3;
+        $entityId = 3;
+        /** @var OptionInterface $optionMock */
+        $optionMock = $this->getMock(OptionInterface::class);
 
-        $option = $this->getMockBuilder('Magento\ConfigurableProduct\Api\Data\OptionInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getProductId'])
-            ->getMockForAbstractClass();
-        $option->expects($this->once())
-            ->method('getProductId')
-            ->willReturn($productId);
+        $this->configurableTypeResource->expects(self::once())
+            ->method('getEntityIdByAttribute')
+            ->with($optionMock)
+            ->willReturn($entityId);
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->productRepositoryMock->expects(self::once())
             ->method('getById')
-            ->with($productId)
+            ->with($entityId)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
-            ->willReturn('simple');
+            ->willReturn(Configurable::TYPE_CODE . '-not');
 
-        $this->model->delete($option);
+        $this->model->delete($optionMock);
     }
 
     /**
@@ -143,29 +170,30 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     public function testDeleteCantSaveProducts()
     {
-        $productId = 3;
+        $entityId = 3;
+        /** @var OptionInterface $optionMock */
+        $optionMock = $this->getMock(OptionInterface::class);
 
-        $option = $this->getMockBuilder('Magento\ConfigurableProduct\Api\Data\OptionInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getProductId'])
-            ->getMockForAbstractClass();
-        $option->expects($this->any())
-            ->method('getProductId')
-            ->willReturn($productId);
+        $this->configurableTypeResource->expects(self::once())
+            ->method('getEntityIdByAttribute')
+            ->with($optionMock)
+            ->willReturn($entityId);
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->productRepositoryMock->expects(self::once())
             ->method('getById')
-            ->with($productId)
+            ->with($entityId)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
             ->willReturn(Configurable::TYPE_CODE);
 
-        $this->configurableTypeResource->expects($this->once())
+        $this->configurableTypeResource->expects(self::once())
             ->method('saveProducts')
             ->with($this->productMock)
             ->willThrowException(new \Exception());
-        $this->model->delete($option);
+
+        $this->model->delete($optionMock);
     }
 
     /**
@@ -174,69 +202,73 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     public function testDeleteCantDeleteOption()
     {
-        $productId = 3;
-        $optionId = 33;
-
-        $option = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
+        $entityId = 3;
+        $optionMock = $this->getMockBuilder(Attribute::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getProductId', 'getId'])
-            ->getMockForAbstractClass();
-        $option->expects($this->any())
-            ->method('getProductId')
-            ->willReturn($productId);
-        $option->expects($this->once())
+            ->getMock();
+
+        $optionMock->expects(self::once())
             ->method('getId')
-            ->willReturn($optionId);
+            ->willReturn(33);
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->configurableTypeResource->expects(self::once())
+            ->method('getEntityIdByAttribute')
+            ->with($optionMock)
+            ->willReturn($entityId);
+
+        $this->productRepositoryMock->expects(self::once())
             ->method('getById')
-            ->with($productId)
+            ->with($entityId)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
             ->willReturn(Configurable::TYPE_CODE);
 
-        $this->configurableTypeResource->expects($this->once())
+        $this->configurableTypeResource->expects(self::once())
             ->method('saveProducts')
             ->with($this->productMock);
-        $this->optionResource->expects($this->once())
+
+        $this->optionResource->expects(self::once())
             ->method('delete')
-            ->with($option)
+            ->with($optionMock)
             ->willThrowException(new \Exception());
-        $this->model->delete($option);
+
+        $this->model->delete($optionMock);
     }
 
     public function testDelete()
     {
-        $productId = 3;
-        $optionId = 33;
-
-        $option = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
+        $entityId = 3;
+        $optionMock = $this->getMockBuilder(Attribute::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getProductId', 'getId'])
-            ->getMockForAbstractClass();
-        $option->expects($this->any())
-            ->method('getProductId')
-            ->willReturn($productId);
-        $option->expects($this->any())
-            ->method('getId')
-            ->willReturn($optionId);
+            ->getMock();
 
-        $this->productRepositoryMock->expects($this->once())
+        $this->configurableTypeResource->expects(self::once())
+            ->method('getEntityIdByAttribute')
+            ->with($optionMock)
+            ->willReturn($entityId);
+
+        $this->productRepositoryMock->expects(self::once())
             ->method('getById')
-            ->with($productId)
+            ->with($entityId)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
             ->willReturn(Configurable::TYPE_CODE);
 
-        $this->configurableTypeResource->expects($this->once())
+        $this->configurableTypeResource->expects(self::once())
             ->method('saveProducts')
             ->with($this->productMock);
-        $this->optionResource->expects($this->once())
+
+        $this->optionResource->expects(self::once())
             ->method('delete')
-            ->with($option);
-        $result = $this->model->delete($option);
+            ->with($optionMock)
+            ->willReturn(true);
+
+        $result = $this->model->delete($optionMock);
+
         $this->assertTrue($result);
     }
 
@@ -246,54 +278,26 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetEmptyExtensionAttribute()
     {
-        $productSku = "configurable";
         $optionId = 3;
+        $productSku = "configurable";
 
-        $this->productRepositoryMock->expects($this->once())
-            ->method('get')
-            ->with($productSku)
-            ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
             ->willReturn(Configurable::TYPE_CODE);
 
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn(null);
-
-        $this->model->get($productSku, $optionId);
-    }
-
-    /**
-     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
-     * @expectedExceptionMessage Requested option doesn't exist: 3
-     */
-    public function testGetOptionIdNotFound()
-    {
-        $productSku = "configurable";
-        $optionId = 3;
-
-        $this->productRepositoryMock->expects($this->once())
+        $this->productRepositoryMock->expects(self::once())
             ->method('get')
             ->with($productSku)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn(Configurable::TYPE_CODE);
 
-        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
-        $optionMock->expects($this->once())
-            ->method('getId')
-            ->willReturn(6);
-        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
-            ->setMethods(['getConfigurableProductOptions'])
-            ->getMock();
-        $productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductOptions')
-            ->willReturn([$optionMock]);
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($productExtensionMock);
+        $optionMock = $this->getMock(OptionInterface::class);
+        $optionMock->expects(self::never())
+            ->method('getId');
+
+        $this->optionLoader->expects(self::once())
+            ->method('load')
+            ->with($this->productMock)
+            ->willReturn([]);
 
         $this->model->get($productSku, $optionId);
     }
@@ -302,45 +306,23 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
     {
         $productSku = "configurable";
 
-        $this->productRepositoryMock->expects($this->once())
-            ->method('get')
-            ->with($productSku)
-            ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
+        $this->productMock->expects(self::once())
             ->method('getTypeId')
             ->willReturn(Configurable::TYPE_CODE);
 
-        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
-        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
-            ->setMethods(['getConfigurableProductOptions'])
-            ->getMock();
-        $productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductOptions')
-            ->willReturn([$optionMock]);
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($productExtensionMock);
-
-        $this->assertEquals([$optionMock], $this->model->getList($productSku));
-    }
-
-    public function testGetListNullExtensionAttribute()
-    {
-        $productSku = "configurable";
-
-        $this->productRepositoryMock->expects($this->once())
+        $this->productRepositoryMock->expects(self::once())
             ->method('get')
             ->with($productSku)
             ->willReturn($this->productMock);
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn(Configurable::TYPE_CODE);
 
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn(null);
+        $optionMock = $this->getMock(OptionInterface::class);
 
-        $this->assertEquals([], $this->model->getList($productSku));
+        $this->optionLoader->expects(self::once())
+            ->method('load')
+            ->with($this->productMock)
+            ->willReturn([$optionMock]);
+
+        $this->assertEquals([$optionMock], $this->model->getList($productSku));
     }
 
     /**
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php
deleted file mode 100644
index 858a696a572a797bfbedd5da47b52ecff52d90f5..0000000000000000000000000000000000000000
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
-
-use Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad;
-
-class AfterProductLoadTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var AfterProductLoad
-     */
-    protected $model;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $optionValueFactory;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $productExtensionFactory;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|Magento\Catalog\Model\Product
-     */
-    protected $productMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $configurableProductTypeInstanceMock;
-
-    protected function setUp()
-    {
-        $this->optionValueFactory = $this->getMock(
-            '\Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory',
-            ['create'],
-            [],
-            '',
-            false
-        );
-        $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $this->configurableProductTypeInstanceMock = $this->getMock(
-            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->productMock->expects($this->any())
-            ->method('getTypeInstance')
-            ->willReturn($this->configurableProductTypeInstanceMock);
-        $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory')
-            ->setMethods(['create'])
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $this->model = new \Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad(
-            $this->productExtensionFactory,
-            $this->optionValueFactory
-        );
-    }
-
-    protected function setupOptions()
-    {
-        $optionValues = [
-            [
-                'value_index' => 5,
-            ],
-            [
-                'value_index' => 6,
-            ],
-        ];
-        $optionMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
-            ->disableOriginalConstructor()
-            ->setMethods(['getOptions', 'setValues'])
-            ->getMock();
-        $optionMock1->expects($this->once())
-            ->method('getOptions')
-            ->willReturn($optionValues);
-
-        $optionValueMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $optionValueMock1->expects($this->once())
-            ->method('setValueIndex')
-            ->with($optionValues[0]['value_index'])
-            ->willReturnSelf();
-
-        $optionValueMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $optionValueMock2->expects($this->once())
-            ->method('setValueIndex')
-            ->with($optionValues[1]['value_index'])
-            ->willReturnSelf();
-
-        $this->optionValueFactory->expects($this->at(0))
-            ->method('create')
-            ->willReturn($optionValueMock1);
-        $optionMock1->expects($this->once())
-            ->method('setValues')
-            ->with([$optionValueMock1, $optionValueMock2]);
-
-        $optionMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
-            ->disableOriginalConstructor()
-            ->setMethods(['getOptions', 'setValues'])
-            ->getMock();
-        $optionMock2->expects($this->once())
-            ->method('getOptions')
-            ->willReturn([]);
-        $optionMock2->expects($this->once())
-            ->method('setValues')
-            ->with([]);
-        $this->optionValueFactory->expects($this->at(1))
-            ->method('create')
-            ->willReturn($optionValueMock2);
-
-        $options = [$optionMock1, $optionMock2];
-
-        $this->configurableProductTypeInstanceMock->expects($this->once())
-            ->method('getConfigurableAttributes')
-            ->with($this->productMock)
-            ->willReturn($options);
-        return $options;
-    }
-
-    protected function setupLinks()
-    {
-        $id = 5;
-        $links = [6, 7];
-        $this->productMock->expects($this->once())
-            ->method('getId')
-            ->willReturn($id);
-        $this->configurableProductTypeInstanceMock->expects($this->once())
-            ->method('getChildrenIds')
-            ->with($id)
-            ->willReturn([$links]);
-        return $links;
-    }
-
-    public function testAfterLoad()
-    {
-        $options = $this->setupOptions();
-        $links = $this->setupLinks();
-
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-
-        $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
-            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
-            ->getMock();
-
-        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions')
-            ->with($options)
-            ->willReturnSelf();
-        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks')
-            ->with($links)
-            ->willReturnSelf();
-
-        $this->productExtensionFactory->expects($this->once())
-            ->method('create')
-            ->willReturn($extensionAttributeMock);
-
-        $this->productMock->expects($this->once())
-            ->method('setExtensionAttributes')
-            ->with($extensionAttributeMock)
-            ->willReturnSelf();
-
-        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
-    }
-
-    public function testAfterLoadNotConfigurableProduct()
-    {
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn('simple');
-
-        $this->productMock->expects($this->never())
-            ->method('getExtensionAttributes');
-        $this->productMock->expects($this->never())
-            ->method('setExtensionAttributes');
-        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
-    }
-
-    public function testAfterLoadNoLinks()
-    {
-        $options = $this->setupOptions();
-
-        $this->productMock->expects($this->once())
-            ->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-
-        $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
-            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
-            ->getMock();
-
-        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions')
-            ->with($options)
-            ->willReturnSelf();
-        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks')
-            ->with([])
-            ->willReturnSelf();
-
-        $this->productExtensionFactory->expects($this->once())
-            ->method('create')
-            ->willReturn($extensionAttributeMock);
-
-        $this->productMock->expects($this->once())
-            ->method('setExtensionAttributes')
-            ->with($extensionAttributeMock)
-            ->willReturnSelf();
-
-        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
-    }
-}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
index 4e812f684634f6e963d56324f2df1e5170b8a0d9..f58f04849ca96fcbf5ffb0282de42d15d2def72f 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
@@ -3,528 +3,306 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
 
+use Magento\Catalog\Api\Data\ProductAttributeInterface;
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\ProductFactory;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
 use Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
+/**
+ * Class AroundProductRepositorySaveTest
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var AroundProductRepositorySave
+     * @var ProductAttributeRepositoryInterface|MockObject
      */
-    protected $plugin;
+    private $productAttributeRepository;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var ProductFactory|MockObject
      */
-    protected $productRepositoryMock;
+    private $productFactory;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Closure
      */
-    protected $productOptionRepositoryMock;
+    private $closure;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Product|MockObject
      */
-    protected $productFactoryMock;
+    private $product;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Product|MockObject
      */
-    protected $productMock;
+    private $result;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var ProductRepositoryInterface|MockObject
      */
-    protected $productExtensionMock;
+    private $productRepository;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var ProductExtensionAttributes|MockObject
      */
-    protected $configurableTypeFactoryMock;
+    private $extensionAttributes;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var ProductAttributeInterface|MockObject
      */
-    protected $productInterfaceMock;
+    private $eavAttribute;
 
     /**
-     * @var \Closure
+     * @var OptionInterface|MockObject
      */
-    protected $closureMock;
+    private $option;
+
+    /**
+     * @var AroundProductRepositorySave
+     */
+    private $plugin;
 
     protected function setUp()
     {
-        $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface');
-        $this->productOptionRepositoryMock = $this->getMock(
-            'Magento\ConfigurableProduct\Api\OptionRepositoryInterface'
-        );
-        $this->configurableTypeFactoryMock = $this->getMock(
-            '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory',
-            ['create'],
-            [],
-            '',
-            false
-        );
-        $this->productInterfaceMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
-        $this->productMock = $this->getMock(
-            'Magento\Catalog\Model\Product',
-            ['getExtensionAttributes', 'getTypeId', 'getSku', 'getStoreId', 'getId', 'getTypeInstance'],
-            [],
-            '',
-            false
-        );
-        $this->closureMock = function () {
-            return $this->productMock;
-        };
+        $this->productAttributeRepository = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class);
 
-        $this->productFactoryMock = $this->getMockBuilder('\Magento\Catalog\Model\ProductFactory')
+        $this->productFactory = $this->getMockBuilder(ProductFactory::class)
+            ->disableOriginalConstructor()
             ->setMethods(['create'])
+            ->getMock();
+
+        $this->product = $this->getMockBuilder(Product::class)
             ->disableOriginalConstructor()
+            ->setMethods(['getTypeId'])
             ->getMock();
-        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-        $this->plugin = $objectManager->getObject(
-            'Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave',
-            [
-                'optionRepository' => $this->productOptionRepositoryMock,
-                'productFactory' => $this->productFactoryMock,
-                'typeConfigurableFactory' => $this->configurableTypeFactoryMock
-            ]
-        );
 
-        $this->productExtensionMock = $this->getMock(
-            'Magento\Catalog\Api\Data\ProductExtension',
-            [
-                'getConfigurableProductOptions',
-                'getConfigurableProductLinks',
-                'setConfigurableProductOptions',
-                'setConfigurableProductLinks',
-            ],
-            [],
-            '',
-            false
-        );
-    }
+        $this->result = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getExtensionAttributes'])
+            ->getMock();
 
-    public function testAroundSaveWhenProductIsSimple()
-    {
-        $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple');
-        $this->productMock->expects($this->never())->method('getExtensionAttributes');
+        $this->closure = function () {
+            return $this->result;
+        };
 
-        $this->assertEquals(
-            $this->productMock,
-            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class);
+
+        $this->extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks'])
+            ->getMockForAbstractClass();
+
+        $this->eavAttribute = $this->getMockForAbstractClass(ProductAttributeInterface::class);
+
+        $this->option = $this->getMockForAbstractClass(OptionInterface::class);
+
+        $this->plugin = new AroundProductRepositorySave(
+            $this->productAttributeRepository,
+            $this->productFactory
         );
     }
 
-    public function testAroundSaveWithoutOptions()
+    public function testAroundSaveWhenProductIsSimple()
     {
-        $this->productInterfaceMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productInterfaceMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductOptions')
-            ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductLinks')
-            ->willReturn(null);
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+        $this->product->expects(static::never())
+            ->method('getExtensionAttributes');
 
         $this->assertEquals(
-            $this->productMock,
-            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock)
+            $this->result,
+            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
         );
     }
 
-    protected function setupProducts($productIds, $attributeCode, $additionalProductId = null)
-    {
-        $count = 0;
-        $products = [];
-        foreach ($productIds as $productId) {
-            $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
-                ->disableOriginalConstructor()
-                ->getMock();
-            $productMock->expects($this->once())
-                ->method('load')
-                ->with($productId)
-                ->willReturnSelf();
-            $productMock->expects($this->once())
-                ->method('getId')
-                ->willReturn($productId);
-            $productMock->expects($this->any())
-                ->method('getData')
-                ->with($attributeCode)
-                ->willReturn($productId);
-            $this->productFactoryMock->expects($this->at($count))
-                ->method('create')
-                ->willReturn($productMock);
-            $products[] = $productMock;
-            $count++;
-        }
-
-        if ($additionalProductId) {
-            $nonExistingProductMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
-                ->disableOriginalConstructor()
-                ->getMock();
-            $nonExistingProductMock->expects($this->once())
-                ->method('load')
-                ->with($additionalProductId)
-                ->willReturnSelf();
-            $this->productFactoryMock->expects($this->at($count))
-                ->method('create')
-                ->willReturn($nonExistingProductMock);
-            $products[] = $nonExistingProductMock;
-        }
-        return $products;
-    }
-
-    protected function setupConfigurableProductAttributes($attributeCodes)
+    public function testAroundSaveWithoutOptions()
     {
-        $configurableProductTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-
-        $this->productMock->expects($this->once())
-            ->method('getTypeInstance')
-            ->willReturn($configurableProductTypeMock);
-
-        $configurableAttributes = [];
-        foreach ($attributeCodes as $attributeCode) {
-            $configurableAttribute = $this->getMockBuilder(
-                '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute'
-            )->setMethods(['getProductAttribute'])
-                ->disableOriginalConstructor()
-                ->getMock();
-            $productAttributeMock = $this->getMockBuilder('\Magento\Catalog\Model\ResourceModel\Eav\Attribute')
-                ->disableOriginalConstructor()
-                ->getMock();
-            $productAttributeMock->expects($this->once())
-                ->method('getAttributeCode')
-                ->willReturn($attributeCode);
-            $configurableAttribute->expects($this->once())
-                ->method('getProductAttribute')
-                ->willReturn($productAttributeMock);
-            $configurableAttributes[] = $configurableAttribute;
-        }
-
-        $configurableProductTypeMock->expects($this->once())
-            ->method('getConfigurableAttributes')
-            ->with($this->productMock)
-            ->willReturn($configurableAttributes);
-
-        return $this;
-    }
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
 
-    public function testAroundSaveWithLinks()
-    {
-        $links = [4, 5];
-        $configurableAttributeCode = 'color';
-        $this->productMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productMock->expects($this->once())
+        $this->result->expects(static::once())
             ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn($this->extensionAttributes);
+
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductOptions')
-            ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn([]);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductLinks')
-            ->willReturn($links);
-
-        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
-        $this->setupProducts($links, $configurableAttributeCode);
-
-        $configurableTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-        $this->configurableTypeFactoryMock->expects($this->once())
-            ->method('create')
-            ->willReturn($configurableTypeMock);
-        $configurableTypeMock->expects($this->once())
-            ->method('saveProducts')
-            ->with($this->productMock, $links);
+            ->willReturn([]);
 
-        $productSku = 'configurable-sku';
-        $this->productMock->expects($this->any())
-            ->method('getSku')
-            ->willReturn($productSku);
-        $newProductMock = $this->setupReload($productSku);
+        $this->productAttributeRepository->expects(static::never())
+            ->method('get');
 
         $this->assertEquals(
-            $newProductMock,
-            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+            $this->result,
+            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
         );
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\InputException
-     * @expectedExceptionMessage Product with id "6" does not exist.
+     * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values.
      */
-    public function testAroundSaveWithNonExistingLinks()
+    public function testAroundSaveWithLinks()
     {
         $links = [4, 5];
-        $nonExistingId = 6;
-        $configurableAttributeCode = 'color';
-
-        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
-        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $nonExistingId);
-        $nonExistingProductMock = $productMocks[2];
-        $nonExistingProductMock->expects($this->once())
-            ->method('getId')
-            ->willReturn(null);
-        $links[] = $nonExistingId;
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
 
-        $this->productMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productMock->expects($this->once())
+        $this->result->expects(static::once())
             ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn($this->extensionAttributes);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductOptions')
             ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductLinks')
             ->willReturn($links);
 
-        $configurableTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-        $this->configurableTypeFactoryMock->expects($this->once())
+        $this->productAttributeRepository->expects(static::never())
+            ->method('get');
+
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['load', 'getData', '__wakeup'])
+            ->getMock();
+
+        $this->productFactory->expects(static::exactly(2))
             ->method('create')
-            ->willReturn($configurableTypeMock);
-        $configurableTypeMock->expects($this->never())
-            ->method('saveProducts')
-            ->with($this->productMock, $links);
+            ->willReturn($product);
 
-        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+        $product->expects(static::exactly(2))
+            ->method('load')
+            ->willReturnSelf();
+        $product->expects(static::never())
+            ->method('getData');
+
+        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\InputException
-     * @expectedExceptionMessage Product with id "6" does not contain required attribute "color".
+     * @expectedExceptionMessage Product with id "4" does not contain required attribute "color".
      */
     public function testAroundSaveWithLinksWithMissingAttribute()
     {
-        $links = [4, 5];
-        $simpleProductId = 6;
-        $configurableAttributeCode = 'color';
-
-        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
-        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId);
-        $simpleProductMock = $productMocks[2];
-        $simpleProductMock->expects($this->once())
-            ->method('getId')
-            ->willReturn($simpleProductId);
-        $simpleProductMock->expects($this->any())
-            ->method('getData')
-            ->with($configurableAttributeCode)
-            ->willReturn(null);
+        $simpleProductId = 4;
+        $links = [$simpleProductId, 5];
+        $attributeCode = 'color';
+        $attributeId = 23;
 
-        $links[] = $simpleProductId;
+        $this->option->expects(static::once())
+            ->method('getAttributeId')
+            ->willReturn($attributeId);
 
-        $this->productMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productMock->expects($this->once())
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
+
+        $this->result->expects(static::once())
             ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn($this->extensionAttributes);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductOptions')
-            ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn([$this->option]);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductLinks')
             ->willReturn($links);
 
-        $configurableTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-        $this->configurableTypeFactoryMock->expects($this->once())
-            ->method('create')
-            ->willReturn($configurableTypeMock);
-        $configurableTypeMock->expects($this->never())
-            ->method('saveProducts')
-            ->with($this->productMock, $links);
-
-        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
-    }
-
-    /**
-     * @expectedException \Magento\Framework\Exception\InputException
-     * @expectedExceptionMessage Products "6" and 4 have the same set of attribute values.
-     */
-    public function testAroundSaveWithLinksWithDuplicateAttributes()
-    {
-        $links = [4, 5];
-        $simpleProductId = 6;
-        $configurableAttributeCode = 'color';
-
-        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
-        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId);
-        $simpleProductMock = $productMocks[2];
-        $simpleProductMock->expects($this->once())
-            ->method('getId')
-            ->willReturn($simpleProductId);
-        $simpleProductMock->expects($this->any())
-            ->method('getData')
-            ->with($configurableAttributeCode)
-            ->willReturn(4);
+        $this->productAttributeRepository->expects(static::once())
+            ->method('get')
+            ->willReturn($this->eavAttribute);
 
-        $links[] = $simpleProductId;
+        $this->eavAttribute->expects(static::once())
+            ->method('getAttributeCode')
+            ->willReturn($attributeCode);
 
-        $this->productMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductOptions')
-            ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
-            ->method('getConfigurableProductLinks')
-            ->willReturn($links);
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['load', 'getData', '__wakeup'])
+            ->getMock();
 
-        $configurableTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-        $this->configurableTypeFactoryMock->expects($this->once())
+        $this->productFactory->expects(static::once())
             ->method('create')
-            ->willReturn($configurableTypeMock);
-        $configurableTypeMock->expects($this->never())
-            ->method('saveProducts')
-            ->with($this->productMock, $links);
+            ->willReturn($product);
+        $product->expects(static::once())
+            ->method('load')
+            ->with($simpleProductId)
+            ->willReturnSelf();
+        $product->expects(static::once())
+            ->method('getData')
+            ->with($attributeCode)
+            ->willReturn(false);
 
-        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\InputException
-     * @expectedExceptionMessage The configurable product does not have any variation attribute.
+     * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values.
      */
-    public function testAroundSaveWithLinksWithoutVariationAttributes()
+    public function testAroundSaveWithLinksWithDuplicateAttributes()
     {
         $links = [4, 5];
+        $attributeCode = 'color';
+        $attributeId = 23;
+
+        $this->option->expects(static::once())
+            ->method('getAttributeId')
+            ->willReturn($attributeId);
 
-        $this->setupConfigurableProductAttributes([]);
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
 
-        $this->productMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        $this->productMock->expects($this->once())
+        $this->result->expects(static::once())
             ->method('getExtensionAttributes')
-            ->willReturn($this->productExtensionMock);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn($this->extensionAttributes);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductOptions')
-            ->willReturn(null);
-        $this->productExtensionMock->expects($this->once())
+            ->willReturn([$this->option]);
+        $this->extensionAttributes->expects(static::once())
             ->method('getConfigurableProductLinks')
             ->willReturn($links);
 
-        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
-    }
-
-    public function testAroundSaveWithOptions()
-    {
-        $productSku = "configurable_sku";
-        $this->productInterfaceMock->expects($this->once())->method('getTypeId')
-            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
-        //two options with id 5 and 6
-        $options = $this->setupOptions([5, 6]);
-        //two existing options with id 4 and 5
-        $this->setupExistingOptions([4, 5]);
-
-        $this->productMock->expects($this->any())->method('getSku')
-            ->will($this->returnValue($productSku));
-
-        $this->productOptionRepositoryMock->expects($this->at(0))
-            ->method('save')
-            ->with($productSku, $options[0]);
-        $this->productOptionRepositoryMock->expects($this->at(1))
-            ->method('save')
-            ->with($productSku, $options[1]);
-        $this->productOptionRepositoryMock->expects($this->at(2))
-            ->method('deleteById')
-            ->with($productSku, 4);
-
-        $configurableProductTypeMock = $this->getMockBuilder(
-            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable'
-        )->disableOriginalConstructor()->getMock();
-        $configurableProductTypeMock->expects($this->once())
-            ->method('resetConfigurableAttributes')
-            ->with($this->productMock)
-            ->willReturnSelf();
-        $this->productMock->expects($this->any())
-            ->method('getTypeInstance')
-            ->willReturn($configurableProductTypeMock);
-
-        $newProductMock = $this->setupReload($productSku);
-
-        $this->assertEquals(
-            $newProductMock,
-            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock)
-        );
-    }
-
-    protected function setupReload($productSku)
-    {
-        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
-            ->disableOriginalConstructor()->getMock();
-        $this->productRepositoryMock->expects($this->once())
+        $this->productAttributeRepository->expects(static::once())
             ->method('get')
-            ->with($productSku, false, null, true)
-            ->willReturn($newProductMock);
-        return $newProductMock;
-    }
+            ->willReturn($this->eavAttribute);
 
-    protected function setupExistingOptions(array $existingOptionIds)
-    {
-        $options = [];
-        foreach ($existingOptionIds as $existingOptionId) {
-            $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
-            $optionMock->expects($this->any())
-                ->method('getId')
-                ->willReturn($existingOptionId);
-            $options[] = $optionMock;
-        }
-
-        $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
-            ->disableOriginalConstructor()
-            ->setMethods(['getConfigurableProductOptions'])
-            ->getMock();
-        $productExtensionMock->expects($this->any())
-            ->method('getConfigurableProductOptions')
-            ->willReturn($options);
-
-        $this->productMock->expects($this->any())
-            ->method('getExtensionAttributes')
-            ->willReturn($productExtensionMock);
-    }
+        $this->eavAttribute->expects(static::once())
+            ->method('getAttributeCode')
+            ->willReturn($attributeCode);
 
-    protected function setupOptions(array $optionIds)
-    {
-        $options = [];
-        foreach ($optionIds as $optionId) {
-            $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
-            $optionMock->expects($this->any())
-                ->method('getId')
-                ->willReturn($optionId);
-            $options[] = $optionMock;
-        }
-
-        $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
+        $product = $this->getMockBuilder(Product::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks'])
+            ->setMethods(['load', 'getData', '__wakeup'])
             ->getMock();
-        $productExtensionMock->expects($this->any())
-            ->method('getConfigurableProductOptions')
-            ->willReturn($options);
-        $productExtensionMock->expects($this->any())
-            ->method('getConfigurableProductLinks')
-            ->willReturn(null);
 
-        $this->productInterfaceMock->expects($this->once())
-            ->method('getExtensionAttributes')
-            ->willReturn($productExtensionMock);
-        return $options;
+        $this->productFactory->expects(static::exactly(2))
+            ->method('create')
+            ->willReturn($product);
+        $product->expects(static::exactly(2))
+            ->method('load')
+            ->willReturnSelf();
+        $product->expects(static::exactly(4))
+            ->method('getData')
+            ->with($attributeCode)
+            ->willReturn($attributeId);
+
+        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php
index f004391098ddd30ba76e5816922858d5ee450d61..f1b203aee546f828ed79bc5229f22d80970872dc 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php
@@ -6,35 +6,53 @@
 
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
 
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Attribute\Backend\Price;
+use Magento\Catalog\Model\Product\Type;
 use Magento\ConfigurableProduct\Model\Plugin\PriceBackend;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
-use Magento\Catalog\Model\Product\Type;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
+/**
+ * Class PriceBackendTest
+ */
 class PriceBackendTest extends \PHPUnit_Framework_TestCase
 {
     const CLOSURE_VALUE = 'CLOSURE';
-    /** @var  PriceBackend */
+
+    /**
+     * @var PriceBackend
+     */
     private $priceBackendPlugin;
-    /** @var  \PHPUnit_Framework_MockObject_MockObject */
-    private $priceAttributeMock;
-    /** @var  \Closure */
+
+    /**
+     * @var Price|MockObject
+     */
+    private $priceAttribute;
+
+    /**
+     * @var \Closure
+     */
     private $closure;
-    /** @var  \PHPUnit_Framework_MockObject_MockObject */
-    private $productMock;
+
+    /**
+     * @var Product|MockObject
+     */
+    private $product;
 
     protected function setUp()
     {
         $objectManager = new ObjectManager($this);
-        $this->priceBackendPlugin = $objectManager->getObject('Magento\ConfigurableProduct\Model\Plugin\PriceBackend');
+        $this->priceBackendPlugin = $objectManager->getObject(PriceBackend::class);
 
         $this->closure = function () {
             return static::CLOSURE_VALUE;
         };
-        $this->priceAttributeMock = $this->getMockBuilder('Magento\Catalog\Model\Product\Attribute\Backend\Price')
+        $this->priceAttribute = $this->getMockBuilder(Price::class)
             ->disableOriginalConstructor()
             ->getMock();
-        $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product')
+        $this->product = $this->getMockBuilder(Product::class)
             ->disableOriginalConstructor()
             ->setMethods(['getTypeId', 'getPriceType', '__wakeUp'])
             ->getMock();
@@ -48,11 +66,13 @@ class PriceBackendTest extends \PHPUnit_Framework_TestCase
      */
     public function testAroundValidate($typeId, $expectedResult)
     {
-        $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue($typeId));
+        $this->product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn($typeId);
         $result = $this->priceBackendPlugin->aroundValidate(
-            $this->priceAttributeMock,
+            $this->priceAttribute,
             $this->closure,
-            $this->productMock
+            $this->product
         );
         $this->assertEquals($expectedResult, $result);
     }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php
new file mode 100644
index 0000000000000000000000000000000000000000..f5f121784801e35fc6b65ce3e96bd78e7d883573
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Product;
+
+use Magento\Catalog\Api\Data\ProductExtensionInterface;
+
+/**
+ * Class ProductExtensionAttributes
+ */
+abstract class ProductExtensionAttributes implements ProductExtensionInterface
+{
+
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php
new file mode 100644
index 0000000000000000000000000000000000000000..c61ad824835c5b234b0408890334184ebb572b76
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Product;
+
+use Magento\Quote\Api\Data\ProductOptionExtensionInterface;
+
+/**
+ * Class ProductOptionExtensionAttributes
+ */
+abstract class ProductOptionExtensionAttributes implements ProductOptionExtensionInterface
+{
+
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..62d14806d85b0b5975c05f11c8d1df896a7126d5
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Product;
+
+use Magento\Catalog\Model\Product;
+use Magento\ConfigurableProduct\Helper\Product\Options\Loader;
+use Magento\ConfigurableProduct\Model\Product\ReadHandler;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class ReadHandlerTest
+ */
+class ReadHandlerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ReadHandler
+     */
+    private $readHandler;
+
+    /**
+     * @var Loader|MockObject
+     */
+    private $optionLoader;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $this->optionLoader = $this->getMockBuilder(Loader::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['load'])
+            ->getMock();
+
+        $this->readHandler = new ReadHandler($this->optionLoader);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Model\Product\ReadHandler::execute
+     */
+    public function testExecuteWithInvalidProductType()
+    {
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeId', 'getExtensionAttributes'])
+            ->getMock();
+
+        $product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+
+        $product->expects(static::never())
+            ->method('getExtensionAttributes');
+
+        $entity = $this->readHandler->execute('Entity', $product);
+        static::assertSame($product, $entity);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Model\Product\ReadHandler::execute
+     */
+    public function testExecute()
+    {
+        $options = [
+            ['value_index' => 12],
+            ['value_index' => 13]
+        ];
+        $entityId = 1;
+        $ids = [1, 2, 3];
+
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods([
+                'getTypeId', 'getId', 'getExtensionAttributes', 'setExtensionAttributes', 'getTypeInstance'
+            ])
+            ->getMock();
+
+        $product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(Configurable::TYPE_CODE);
+
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
+            ->getMockForAbstractClass();
+
+        $product->expects(static::once())
+            ->method('getExtensionAttributes')
+            ->willReturn($extensionAttributes);
+
+        $this->optionLoader->expects(static::once())
+            ->method('load')
+            ->with($product)
+            ->willReturn($options);
+
+        $typeInstance = $this->getMockBuilder(Configurable::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getChildrenIds'])
+            ->getMock();
+
+        $product->expects(static::once())
+            ->method('getTypeInstance')
+            ->willReturn($typeInstance);
+
+        $product->expects(static::once())
+            ->method('getId')
+            ->willReturn($entityId);
+
+        $typeInstance->expects(static::once())
+            ->method('getChildrenIds')
+            ->willReturn($ids);
+
+        $product->expects(static::once())
+            ->method('setExtensionAttributes')
+            ->with($extensionAttributes);
+
+        $entity = $this->readHandler->execute('Entity', $product);
+        static::assertSame($product, $entity);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2dd2d432669886504f64ee4053d318a542fad95e
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Product;
+
+use Magento\Catalog\Api\Data\ProductAttributeInterface;
+use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\ProductRepository;
+use Magento\ConfigurableProduct\Api\Data\OptionInterface;
+use Magento\ConfigurableProduct\Model\OptionRepository;
+use Magento\ConfigurableProduct\Model\Product\SaveHandler;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableModel;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
+use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class SaveHandlerTest
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class SaveHandlerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var OptionRepository|MockObject
+     */
+    private $optionRepository;
+
+    /**
+     * @var ConfigurableFactory|MockObject
+     */
+    private $configurableFactory;
+
+    /**
+     * @var Configurable|MockObject
+     */
+    private $configurable;
+
+    /**
+     * @var ProductAttributeRepositoryInterface|MockObject
+     */
+    private $productAttributeRepository;
+
+    /**
+     * @var SaveHandler
+     */
+    private $saveHandler;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $this->optionRepository = $this->getMockBuilder(OptionRepository::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['save', 'getList', 'deleteById'])
+            ->getMock();
+
+        $this->initConfigurableFactoryMock();
+
+        $this->productAttributeRepository = $this->getMock(ProductAttributeRepositoryInterface::class);
+
+        $this->saveHandler = new SaveHandler(
+            $this->configurable,
+            $this->optionRepository,
+            $this->productAttributeRepository
+        );
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute
+     */
+    public function testExecuteWithInvalidProductType()
+    {
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeId', 'getExtensionAttributes'])
+            ->getMock();
+
+        $product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+
+        $product->expects(static::never())
+            ->method('getExtensionAttributes');
+
+        $entity = $this->saveHandler->execute('Entity', $product);
+        static::assertSame($product, $entity);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute
+     */
+    public function testExecuteWithEmptyExtensionAttributes()
+    {
+        $sku = 'test';
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeId', 'getExtensionAttributes', 'getSku'])
+            ->getMock();
+
+        $product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(ConfigurableModel::TYPE_CODE);
+        $product->expects(static::exactly(1))
+            ->method('getSku')
+            ->willReturn($sku);
+
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks'])
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $product->expects(static::once())
+            ->method('getExtensionAttributes')
+            ->willReturn($extensionAttributes);
+
+        $extensionAttributes->expects(static::once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn([]);
+        $extensionAttributes->expects(static::once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn([]);
+
+        $this->optionRepository->expects(static::once())
+            ->method('getList')
+            ->with($sku)
+            ->willReturn([]);
+        $this->optionRepository->expects(static::never())
+            ->method('deleteById');
+
+        $this->productAttributeRepository->expects(static::never())
+            ->method('get');
+
+        $entity = $this->saveHandler->execute('Entity', $product);
+        static::assertSame($product, $entity);
+    }
+
+    /**
+     * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute
+     */
+    public function testExecute()
+    {
+        $attributeId = 90;
+        $sku = 'config-1';
+        $id = 25;
+        $configurableProductLinks = [1, 2, 3];
+
+        $product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeId', 'getSku', 'getData', 'getExtensionAttributes'])
+            ->getMock();
+        $product->expects(static::once())
+            ->method('getTypeId')
+            ->willReturn(ConfigurableModel::TYPE_CODE);
+        $product->expects(static::exactly(2))
+            ->method('getSku')
+            ->willReturn($sku);
+
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
+            ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks'])
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $product->expects(static::once())
+            ->method('getExtensionAttributes')
+            ->willReturn($extensionAttributes);
+
+        $attribute = $this->getMockBuilder(Attribute::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getAttributeId', 'loadByProductAndAttribute'])
+            ->getMock();
+        $this->processSaveOptions($attribute, $product, $attributeId, $sku, $id);
+
+        $option = $this->getMockForAbstractClass(OptionInterface::class);
+        $option->expects(static::once())
+            ->method('getId')
+            ->willReturn($id);
+
+        $list = [$option];
+        $this->optionRepository->expects(static::once())
+            ->method('getList')
+            ->with($sku)
+            ->willReturn($list);
+        $this->optionRepository->expects(static::never())
+            ->method('deleteById');
+
+        $configurableAttributes = [
+            $attribute
+        ];
+        $extensionAttributes->expects(static::once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn($configurableAttributes);
+
+        $extensionAttributes->expects(static::once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($configurableProductLinks);
+
+        $this->configurable->expects(static::once())
+            ->method('saveProducts')
+            ->with($product, $configurableProductLinks);
+
+        $entity = $this->saveHandler->execute('Entity', $product);
+        static::assertSame($product, $entity);
+    }
+
+    /**
+     * Init mock object for configurable factory
+     *
+     * @return void
+     */
+    private function initConfigurableFactoryMock()
+    {
+        $this->configurable = $this->getMockBuilder(Configurable::class)
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+
+        $this->configurableFactory = $this->getMockBuilder(ConfigurableFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->configurableFactory->expects(static::any())
+            ->method('create')
+            ->willReturn($this->configurable);
+    }
+
+    /**
+     * Mock for options save
+     *
+     * @param MockObject $attribute
+     * @param MockObject $product
+     * @param $attributeId
+     * @param $sku
+     * @param $id
+     * @return void
+     */
+    private function processSaveOptions(MockObject $attribute, MockObject $product, $attributeId, $sku, $id)
+    {
+        $attribute->expects(static::once())
+            ->method('getAttributeId')
+            ->willReturn($attributeId);
+
+        $eavAttribute = $this->getMock(ProductAttributeInterface::class);
+        $this->productAttributeRepository->expects(static::once())
+            ->method('get')
+            ->with($attributeId)
+            ->willReturn($eavAttribute);
+        $attribute->expects(static::once())
+            ->method('loadByProductAndAttribute')
+            ->with($product, $eavAttribute)
+            ->willReturnSelf();
+
+        $this->optionRepository->expects(static::once())
+            ->method('save')
+            ->with($sku, $attribute)
+            ->willReturn($id);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
index 4b88a4cef8894f34c075160330fac17c9277d6e7..ef858285e1b8035e30d9867a909ea61b5892fc18 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
@@ -8,7 +8,11 @@
 
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Type;
 
+use Magento\Catalog\Api\Data\ProductExtensionInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\Framework\Model\Entity\EntityMetadata;
+use Magento\Framework\Model\Entity\MetadataPool;
 
 /**
  * Class \Magento\ConfigurableProduct\Test\Unit\Model\Product\Type\ConfigurableTest
@@ -68,6 +72,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     protected $extensionAttributesJoinProcessorMock;
 
+    /**
+     * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadataPool;
+
+    /**
+     * @var EntityMetadata|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $entityMetadata;
+
     /**
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
@@ -120,6 +134,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $this->entityMetadata = $this->getMockBuilder(EntityMetadata::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->metadataPool = $this->getMockBuilder(MetadataPool::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->metadataPool->expects($this->any())
+            ->method('getMetadata')
+            ->with(ProductInterface::class)
+            ->willReturn($this->entityMetadata);
 
         $this->_model = $this->_objectHelper->getObject(
             'Magento\ConfigurableProduct\Model\Product\Type\Configurable',
@@ -135,6 +159,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
                 'logger' => $logger,
                 'productRepository' => $this->productRepository,
                 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock,
+                'metadataPool' => $this->metadataPool,
             ]
         );
     }
@@ -170,13 +195,26 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue($this->attributeData));
         $product->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(true));
         $product->expects($this->any())->method('getStoreId')->will($this->returnValue(1));
-        $product->expects($this->any())->method('getId')->will($this->returnValue(1));
         $product->expects($this->any())->method('getAssociatedProductIds')->will($this->returnValue([2]));
         $product->expects($this->any())->method('hasData')->with('_cache_instance_used_product_attribute_ids')
             ->will($this->returnValue(true));
-        $product->expects($this->any())->method('getData')->with('_cache_instance_used_product_attribute_ids')
-            ->will($this->returnValue([1]));
-
+        $extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class)
+            ->setMethods([
+                'getConfigurableProductOptions',
+                'getConfigurableProductLinks'
+            ])
+            ->getMockForAbstractClass();
+        $this->entityMetadata->expects(static::once())
+            ->method('getLinkField')
+            ->willReturn('link');
+        $dataMap = [
+            ['extension_attributes', null, $extensionAttributes],
+            ['_cache_instance_used_product_attribute_ids', null, 1],
+            ['link', null, 1],
+        ];
+        $product->expects($this->any())
+            ->method('getData')
+            ->willReturnMap($dataMap);
         $attribute = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
             ->disableOriginalConstructor()
             ->setMethods(['addData', 'setStoreId', 'setProductId', 'save', '__wakeup', '__sleep'])
@@ -644,18 +682,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf());
         $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf());
         $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock);
-        $firstItemMock->expects($this->once())->method('getId')->willReturn(false);
-        $productMock->expects($this->at(0))
-            ->method('getData')
-            ->with('_cache_instance_store_filter')
-            ->willReturn('some_filter');
+        $firstItemMock->expects(static::once())
+            ->method('getId')
+            ->willReturn(false);
+        $dataMap = [
+            ['_cache_instance_store_filter', null, 'some_filter'],
+            ['_cache_instance_products', null, [$usedProductMock]],
+            ['_cache_instance_product_set_attributes', null, [$eavAttributeMock]],
+        ];
+        $productMock->expects($this->any())->method('getData')->willReturnMap($dataMap);
         $productMock->expects($this->any())->method('hasData')->willReturn(true);
-        $productMock->expects($this->at(3))->method('getData')
-            ->with('_cache_instance_products')
-            ->willReturn([$usedProductMock]);
-        $productMock->expects($this->at(5))->method('getData')
-            ->with('_cache_instance_product_set_attributes')
-            ->willReturn([$eavAttributeMock]);
         $eavAttributeMock->expects($this->once())->method('getId')->willReturn(1);
         $eavAttributeMock->expects($this->once())->method('getAttributeCode')->willReturn('attr_code');
         $usedProductMock->expects($this->once())
@@ -701,9 +737,12 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf());
         $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf());
         $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock);
-        $firstItemMock->expects($this->any())->method('getId')->willReturn(3);
+        $firstItemMock->expects(static::once())
+            ->method('getId')
+            ->willReturn(3);
         $this->productRepository->expects($this->once())->method('getById')->with(3)->willReturn($firstItemMock);
 
+
         $this->assertEquals(
             $firstItemMock,
             $this->_model->getProductByAttributes($this->attributeData, $productMock)
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php
index 6c1eff26cb135e2c1f815ac6e9634073a3b5390b..a84c625f82fdc24fe91f2995937bbe88cc069d0e 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php
@@ -5,6 +5,9 @@
  */
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Quote\Item;
 
+use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductOptionExtensionAttributes;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
 class CartItemProcessorTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -13,25 +16,30 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase
     protected $model;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var MockObject
      */
     protected $objectFactoryMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var MockObject
      */
     protected $optionFactoryMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var MockObject
      */
     protected $optionExtensionFactoryMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var MockObject
      */
     protected $optionValueFactoryMock;
 
+    /**
+     * @var ProductOptionExtensionAttributes|MockObject
+     */
+    private $productOptionExtensionAttributes;
+
     protected function setUp()
     {
         $this->objectFactoryMock = $this->getMock('\Magento\Framework\DataObject\Factory', ['create'], [], '', false);
@@ -57,6 +65,11 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase
             false
         );
 
+        $this->productOptionExtensionAttributes = $this->getMockBuilder(ProductOptionExtensionAttributes::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setConfigurableItemOptions'])
+            ->getMock();
+
         $this->model = new \Magento\ConfigurableProduct\Model\Quote\Item\CartItemProcessor(
             $this->objectFactoryMock,
             $this->optionFactoryMock,
@@ -156,21 +169,16 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase
         $this->optionFactoryMock->expects($this->once())->method('create')->willReturn($productOptionMock);
         $productOptionMock->expects($this->once())->method('getExtensionAttributes')->willReturn(null);
 
-        $extAttributeMock = $this->getMock(
-            '\Magento\Quote\Api\Data\ProductOptionExtension',
-            ['setConfigurableItemOptions'],
-            [],
-            '',
-            false
-        );
-        $this->optionExtensionFactoryMock->expects($this->once())->method('create')->willReturn($extAttributeMock);
-        $extAttributeMock->expects($this->once())
+        $this->optionExtensionFactoryMock->expects(static::once())
+            ->method('create')
+            ->willReturn($this->productOptionExtensionAttributes);
+        $this->productOptionExtensionAttributes->expects($this->once())
             ->method('setConfigurableItemOptions')
             ->with([$optionValueMock])
             ->willReturnSelf();
         $productOptionMock->expects($this->once())
             ->method('setExtensionAttributes')
-            ->with($extAttributeMock)
+            ->with($this->productOptionExtensionAttributes)
             ->willReturnSelf();
         $cartItemMock->expects($this->once())->method('setProductOption')->with($productOptionMock)->willReturnSelf();
 
@@ -200,27 +208,22 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase
         $optionValueMock->expects($this->once())->method('setOptionId')->with($optionId)->willReturnSelf();
         $optionValueMock->expects($this->once())->method('setOptionValue')->with($optionValue)->willReturnSelf();
 
-        $extAttributeMock = $this->getMock(
-            '\Magento\Quote\Api\Data\ProductOptionExtension',
-            ['setConfigurableItemOptions'],
-            [],
-            '',
-            false
-        );
-        $extAttributeMock->expects($this->once())
+        $this->productOptionExtensionAttributes->expects($this->once())
             ->method('setConfigurableItemOptions')
             ->with([$optionValueMock])
             ->willReturnSelf();
 
         $productOptionMock = $this->getMock('\Magento\Quote\Api\Data\ProductOptionInterface');
-        $productOptionMock->expects($this->exactly(2))->method('getExtensionAttributes')->willReturn($extAttributeMock);
+        $productOptionMock->expects(static::exactly(2))
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productOptionExtensionAttributes);
 
         $cartItemMock->expects($this->once())->method('getProduct')->willReturn($productMock);
         $cartItemMock->expects($this->exactly(2))->method('getProductOption')->willReturn($productOptionMock);
 
         $productOptionMock->expects($this->once())
             ->method('setExtensionAttributes')
-            ->with($extAttributeMock)
+            ->with($this->productOptionExtensionAttributes)
             ->willReturnSelf();
         $cartItemMock->expects($this->once())->method('setProductOption')->with($productOptionMock)->willReturnSelf();
 
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
index 833ca223a7b9b7ebf1ccc0cde42de3b63e5a166a..92603ce718c92b8e266de24e7285744e5ef98f6d 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
@@ -6,12 +6,15 @@
 
 namespace Magento\ConfigurableProduct\Test\Unit\Model\ResourceModel\Product\Type;
 
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;
+use Magento\Framework\Model\ResourceModel\Db\Context;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
 
 class ConfigurableTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable
+     * @var Configurable
      */
     protected $configurable;
 
@@ -35,6 +38,11 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     protected $metadataPool;
 
+    /**
+     * @var \Magento\Framework\Model\Entity\EntityMetadata|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadata;
+
     protected function setUp()
     {
         $connectionMock = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface')->getMock();
@@ -44,13 +52,13 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
 
         $this->relation = $this->getMock('Magento\Catalog\Model\ResourceModel\Product\Relation', [], [], '', false);
 
-        $metadata = $this->getMock('Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false);
+        $this->metadata = $this->getMock('Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false);
 
         $this->metadataPool = $this->getMock('Magento\Framework\Model\Entity\MetadataPool', [], [], '', false);
         $this->metadataPool->expects($this->any())
             ->method('getMetadata')
-            ->with(\Magento\Catalog\Api\Data\ProductInterface::class)
-            ->willReturn($metadata);
+            ->with(ProductInterface::class)
+            ->willReturn($this->metadata);
 
         $this->objectManagerHelper = new ObjectManagerHelper($this);
         $this->configurable = $this->objectManagerHelper->getObject(
@@ -65,30 +73,19 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
 
     public function testSaveProducts()
     {
+        /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $mainProduct */
         $mainProduct = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getIsDuplicate', '__sleep', '__wakeup', 'getTypeInstance', 'getConnection'])
+            ->setMethods(['__sleep', '__wakeup', 'getData'])
             ->disableOriginalConstructor()
             ->getMock();
-        $mainProduct->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(false));
-
-        $typeInstance = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable')
-            ->disableOriginalConstructor()->getMock();
-        $typeInstance->expects($this->once())->method('getUsedProductIds')->will($this->returnValue([1]));
 
-        $mainProduct->expects($this->once())->method('getTypeInstance')->will($this->returnValue($typeInstance));
-
-        $this->configurable->saveProducts($mainProduct, [1, 2, 3]);
-    }
-
-    public function testSaveProductsForDuplicate()
-    {
-        $mainProduct = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getIsDuplicate', '__sleep', '__wakeup', 'getTypeInstance', 'getConnection'])
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $mainProduct->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(true));
-        $mainProduct->expects($this->never())->method('getTypeInstance')->will($this->returnSelf());
+        $this->metadata->expects($this->once())
+            ->method('getLinkField')
+            ->willReturn('link');
+        $mainProduct->expects($this->once())
+            ->method('getData')
+            ->with('link')
+            ->willReturn(3);
 
         $this->configurable->saveProducts($mainProduct, [1, 2, 3]);
     }
@@ -98,40 +95,42 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetConfigurableOptions()
     {
-        $configurable = $this->getMock(
-            'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable',
-            [
-                'getTable',
-                'getConnection',
-            ],
-            [
-                $this->resource,
-                $this->relation,
-            ],
-            '',
-            false
-        );
+        $context = $this->getMockBuilder(Context::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        /** @var Configurable|\PHPUnit_Framework_MockObject_MockObject $configurable */
+        $configurable = $this->getMockBuilder(Configurable::class)
+            ->setMethods(['getTable', 'getConnection'])
+            ->setConstructorArgs([$context, $this->relation, $this->metadataPool])
+            ->getMock();
 
+        /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */
         $product = $this->getMockBuilder('Magento\Catalog\Model\Product')
             ->setMethods(
                 [
                     '__sleep',
                     '__wakeup',
-                    'getId',
+                    'getData',
                 ]
             )
             ->disableOriginalConstructor()
             ->getMock();
+
+        $this->metadata->expects(self::exactly(2))
+            ->method('getLinkField')
+            ->willReturn('link');
         $product->expects($this->once())
-            ->method('getId')
+            ->method('getData')
+            ->with('link')
             ->willReturn('getId value');
 
-        $configurable->expects($this->exactly(6))
+        $configurable->expects($this->exactly(7))
             ->method('getTable')
             ->will(
                 $this->returnValueMap(
                     [
                         ['catalog_product_super_attribute', 'catalog_product_super_attribute value'],
+                        ['catalog_product_entity', 'catalog_product_entity value'],
                         ['catalog_product_super_link', 'catalog_product_super_link value'],
                         ['eav_attribute', 'eav_attribute value'],
                         ['catalog_product_entity', 'catalog_product_entity value'],
@@ -158,7 +157,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
                 ['super_attribute' => 'catalog_product_super_attribute value'],
                 [
                     'sku' => 'entity.sku',
-                    'product_id' => 'super_attribute.product_id',
+                    'product_id' => 'product_entity.entity_id',
                     'attribute_code' => 'attribute.attribute_code',
                     'option_title' => 'option_value.value',
                     'super_attribute_label' =>  'attribute_label.value'
@@ -186,10 +185,15 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             $superAttribute,
         ];
 
-        $select->expects($this->exactly(4))
+        $select->expects($this->exactly(5))
             ->method('joinInner')
             ->will($this->returnSelf())
             ->withConsecutive(
+                [
+                    ['product_entity' => 'catalog_product_entity value'],
+                    'product_entity.link = super_attribute.product_id',
+                    []
+                ],
                 [
                     ['product_link' => 'catalog_product_super_link value'],
                     'product_link.parent_id = super_attribute.product_id',
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index f0d2eaa04326e47b7cc8e72f121de5f19879daf8..c0622f73afe82c653383d45e50a5542a6fa9cb76 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -56,14 +56,28 @@
             </argument>
         </arguments>
     </type>
-    <type name="Magento\Catalog\Model\Product\Type">
-        <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" />
-    </type>
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
         <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/>
     </type>
-    <type name="Magento\Catalog\Model\Product">
-        <plugin name="configurableProductLoadAfter" type="\Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad"/>
+    <type name="Magento\Catalog\Model\Product\Type">
+        <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" />
+    </type>
+    <type name="Magento\Framework\Model\ResourceModel\Db\Relation\ActionPool">
+        <arguments>
+            <argument name="relationActions" xsi:type="array">
+                <item name="Magento\Catalog\Api\Data\ProductInterface" xsi:type="array">
+                    <item name="create" xsi:type="array">
+                        <item name="create_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\SaveHandler</item>
+                    </item>
+                    <item name="update" xsi:type="array">
+                        <item name="update_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\SaveHandler</item>
+                    </item>
+                    <item name="read" xsi:type="array">
+                        <item name="read_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\ReadHandler</item>
+                    </item>
+                </item>
+            </argument>
+        </arguments>
     </type>
     <virtualType name="Magento\ConfigurableProduct\Pricing\Price\Pool" type="Magento\Framework\Pricing\Price\Pool">
         <arguments>
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml
index 6b9ba6a541a2f283c26ade1dc83556145cb9949d..c12f75c35f01931729da2fff72760e28b9377d72 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml
@@ -14,12 +14,20 @@
 <?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?>
 <?php if (($_product->isSaleable() || $_skipSaleableCheck) && count($_attributes)):?>
 <fieldset id="catalog_product_composite_configure_fields_configurable" class="fieldset admin__fieldset">
-    <legend class="legend admin__legend"><span><?php /* @escapeNotVerified */ echo __('Associated Products') ?></span></legend>
+    <legend class="legend admin__legend">
+        <span><?php /* @escapeNotVerified */ echo __('Associated Products') ?></span>
+    </legend>
     <div class="product-options">
         <div class="field admin__field _required required">
             <?php foreach ($_attributes as $_attribute): ?>
-                <label class="label admin__field-label"><?php /* @escapeNotVerified */ echo $_attribute->getLabel() ?></label>
-                <div class="control admin__field-control <?php if ($_attribute->getDecoratedIsLast()):?> last<?php endif; ?>">
+                <label class="label admin__field-label"><?php
+                    /* @escapeNotVerified */ echo $_attribute->getProductAttribute()
+                        ->getStoreLabel($_product->getStoreId());
+                    ?></label>
+                <div class="control admin__field-control <?php
+                    if ($_attribute->getDecoratedIsLast()):
+                        ?> last<?php
+                    endif; ?>">
                     <select name="super_attribute[<?php /* @escapeNotVerified */ echo $_attribute->getAttributeId() ?>]"
                             id="attribute<?php /* @escapeNotVerified */ echo $_attribute->getAttributeId() ?>"
                             class="admin__control-select required-entry super-attribute-select">
diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php
new file mode 100644
index 0000000000000000000000000000000000000000..fcd55c590dc50602320154a5cec79784463792b7
--- /dev/null
+++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Customer\Ui\Component\Listing\Column;
+
+use Magento\Framework\View\Element\UiComponent\ContextInterface;
+use Magento\Framework\View\Element\UiComponentFactory;
+use Magento\Ui\Component\Listing\Columns\Column;
+use Magento\Store\Model\StoreManagerInterface as StoreManager;
+
+/**
+ * Class Websites
+ */
+class Websites extends Column
+{
+    /**
+     * Store manager
+     *
+     * @var StoreManager
+     */
+    protected $storeManager;
+
+    /**
+     * @param ContextInterface $context
+     * @param UiComponentFactory $uiComponentFactory
+     * @param StoreManager $storeManager
+     * @param array $components
+     * @param array $data
+     */
+    public function __construct(
+        ContextInterface $context,
+        UiComponentFactory $uiComponentFactory,
+        StoreManager $storeManager,
+        array $components = [],
+        array $data = []
+    ) {
+        $this->storeManager = $storeManager;
+        parent::__construct($context, $uiComponentFactory, $components, $data);
+    }
+
+    /**
+     * Prepare component configuration
+     * @return void
+     */
+    public function prepare()
+    {
+        parent::prepare();
+        if ($this->storeManager->isSingleStoreMode()) {
+            $this->_data['config']['componentDisabled'] = true;
+        }
+    }
+}
diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml
index c6aa8a420e34216be37a3a5af03c67cdf42ce4cd..67bab36c9ccde0e3ec470b51523859b352d76239 100644
--- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml
+++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml
@@ -245,7 +245,7 @@
                 </item>
             </argument>
         </column>
-        <column name="website_id">
+        <column name="website_id" class="Magento\Customer\Ui\Component\Listing\Column\Websites">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
                     <item name="filter" xsi:type="string">select</item>
diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
index a608dc3809e2f77360430f31dca8869eed8022df..c2f390e90e5d6e5330ca2f3db99bb80074da12a7 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
@@ -133,7 +133,7 @@ class UpdateHandler
                 }
                 if ((!array_key_exists($attribute->getAttributeCode(), $snapshot)
                     || $snapshot[$attribute->getAttributeCode()] === false)
-                    && !empty($data[$attribute->getAttributeCode()])
+                    && array_key_exists($attribute->getAttributeCode(), $data)
                     && !$attribute->isValueEmpty($data[$attribute->getAttributeCode()])
                 ) {
                     $this->attributePersistor->registerInsert(
@@ -146,7 +146,7 @@ class UpdateHandler
                 }
                 if (array_key_exists($attribute->getAttributeCode(), $snapshot)
                     && $snapshot[$attribute->getAttributeCode()] !== false
-                    && !empty($data[$attribute->getAttributeCode()])
+                    && array_key_exists($attribute->getAttributeCode(), $data)
                     && $snapshot[$attribute->getAttributeCode()] != $data[$attribute->getAttributeCode()]
                     && !$attribute->isValueEmpty($data[$attribute->getAttributeCode()])
                 ) {
diff --git a/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php b/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php
index ca30141e79a3277af049df340c3f68fb263508d0..be7b2ed01f72802008880444a3320ad5b496db14 100644
--- a/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php
+++ b/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php
@@ -32,7 +32,7 @@ class Multishipping
     /**
      * @param \Magento\Multishipping\Model\Checkout\Type\Multishipping $subject
      * @param array|null $methods
-     * @return $this
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function beforeSetShippingMethods(
diff --git a/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php b/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php
index 6359573d0d896ee41b510c2ff641c76614bbe261..cad727dbff21aabaf726f37146554dbc190a7596 100644
--- a/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php
+++ b/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php
@@ -43,6 +43,8 @@ class DataProvider
     {
         $result['category_view_optimization']['arguments']['data']['disabled'] =
             !$this->_helper->isGoogleExperimentActive();
+        $result['category_view_optimization']['arguments']['data']['config']['componentType'] =
+            \Magento\Ui\Component\Form\Fieldset::NAME;
 
         return $result;
     }
diff --git a/app/code/Magento/GoogleOptimizer/etc/module.xml b/app/code/Magento/GoogleOptimizer/etc/module.xml
index cddd051c5a53ef58ec70d9a35aedcd902662b2ef..4d0be0af435e075a6d7ab2f0065d8ca3966394e4 100644
--- a/app/code/Magento/GoogleOptimizer/etc/module.xml
+++ b/app/code/Magento/GoogleOptimizer/etc/module.xml
@@ -11,6 +11,7 @@
             <module name="Magento_GoogleAnalytics"/>
             <module name="Magento_Catalog"/>
             <module name="Magento_Cms"/>
+            <module name="Magento_Ui"/>
         </sequence>
     </module>
 </config>
diff --git a/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml b/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b568252e3cc32328769d074ac555a679f2f1b05f
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
+    <fieldset name="actions">
+        <field name="simple_free_shipping">
+            <argument name="data" xsi:type="array">
+                <item name="options" xsi:type="object">Magento\OfflineShipping\Model\Source\SalesRule\FreeShippingOptions</item>
+                <item name="config" xsi:type="array">
+                    <item name="dataType" xsi:type="string">text</item>
+                    <item name="label" xsi:type="string" translate="true">Free Shipping</item>
+                    <item name="formElement" xsi:type="string">select</item>
+                    <item name="dataScope" xsi:type="string">simple_free_shipping</item>
+                    <item name="caption" xsi:type="string" translate="true">-- Please Select --</item>
+                    <item name="sortOrder" xsi:type="number">80</item>
+                </item>
+            </argument>
+        </field>
+    </fieldset>
+</form>
diff --git a/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php b/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php
index 79df0429fbf6fca31b7ac68d2f5522aa1f0cf190..457f77496bd32489d3f87e2e93f606aeadaf2a32 100644
--- a/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php
+++ b/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php
@@ -27,11 +27,10 @@ class Export extends \Magento\Config\Block\System\Config\Form\Field
             'varnish' => $this->getVarnishVersion()
         ];
 
-        $url = $this->getUrl("*/PageCache/exportVarnishConfig", $params);
         $data = [
             'id' => 'system_full_page_cache_varnish_export_button_version' . $this->getVarnishVersion(),
-            'label' => __('Export VCL for Varnish ') . $this->getVarnishVersion(),
-            'onclick' => "setLocation('" . $url . "')",
+            'label' => $this->getLabel(),
+            'onclick' => "setLocation('" . $this->getVarnishUrl($params) . "')",
         ];
 
         $html = $buttonBlock->setData($data)->toHtml();
@@ -48,11 +47,30 @@ class Export extends \Magento\Config\Block\System\Config\Form\Field
         return 0;
     }
 
+    /**
+     * @return \Magento\Framework\Phrase
+     */
+    private function getLabel()
+    {
+        return  __('Export VCL for Varnish %1', $this->getVarnishVersion());
+    }
+
+    /**
+     * @param array $params
+     *
+     * @return string
+     */
+    private function getVarnishUrl($params = [])
+    {
+        return $this->getUrl('*/PageCache/exportVarnishConfig', $params);
+    }
+
     /**
      * Return PageCache TTL value from config
      * to avoid saving empty field
      *
      * @return string
+     * @deprecated
      */
     public function getTtlValue()
     {
diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php
index d788275f40fffecfe12e82b07abb964f818c26ef..0c62c8911ae369d6271398a3669f50f62950ec04 100644
--- a/app/code/Magento/Quote/Model/QuoteRepository.php
+++ b/app/code/Magento/Quote/Model/QuoteRepository.php
@@ -6,11 +6,13 @@
 namespace Magento\Quote\Model;
 
 use Magento\Framework\Api\SortOrder;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Quote\Model\Quote;
 use Magento\Store\Model\StoreManagerInterface;
 use Magento\Framework\Api\Search\FilterGroup;
 use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection;
+use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
 
@@ -62,6 +64,7 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface
      * @param \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection
      * @param \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory
      * @param JoinProcessorInterface $extensionAttributesJoinProcessor
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function __construct(
         QuoteFactory $quoteFactory,
@@ -73,7 +76,6 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface
         $this->quoteFactory = $quoteFactory;
         $this->storeManager = $storeManager;
         $this->searchResultsDataFactory = $searchResultsDataFactory;
-        $this->quoteCollection = $quoteCollection;
         $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
     }
 
@@ -173,11 +175,27 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface
         return $quote;
     }
 
+    /**
+     * Get quote collection
+     * Temporary method to support release backward compatibility.
+     *
+     * @deprecated
+     * @return QuoteCollection
+     */
+    protected function getQuoteCollection()
+    {
+        /** @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $collectionFactory */
+        $collectionFactory = ObjectManager::getInstance()->get(QuoteCollectionFactory::class);
+        return $collectionFactory->create();
+    }
+
     /**
      * {@inheritdoc}
      */
     public function getList(\Magento\Framework\Api\SearchCriteria $searchCriteria)
     {
+        $this->quoteCollection = $this->getQuoteCollection();
+        /** @var \Magento\Quote\Api\Data\CartSearchResultsInterface $searchData */
         $searchData = $this->searchResultsDataFactory->create();
         $searchData->setSearchCriteria($searchCriteria);
 
diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php
index 4b4cd113eb70b6fab4b871e674d49e5fa32839be..40d58b4ea521d831facd52af0c0a09b73af54188 100644
--- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php
+++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php
@@ -366,7 +366,6 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
         $sortOrderMock->expects($this->once())->method('getDirection')->will($this->returnValue($direction));
         $this->quoteCollectionMock->expects($this->once())->method('addOrder')->with('id', $expectedDirection);
 
-
         $searchCriteriaMock->expects($this->once())->method('getCurrentPage')->will($this->returnValue(1));
         $searchCriteriaMock->expects($this->once())->method('getPageSize')->will($this->returnValue(10));
         $this->quoteCollectionMock->expects($this->once())->method('setCurPage')->with(1);
@@ -381,6 +380,18 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->quoteCollectionMock->expects($this->once())->method('getItems')->willReturn([$cartMock]);
         $searchResult->expects($this->once())->method('setItems')->with([$cartMock]);
 
+        $this->model = $this->getMock(
+            'Magento\Quote\Model\QuoteRepository',
+            ['getQuoteCollection'],
+            [
+                'quoteFactory' => $this->quoteFactoryMock,
+                'storeManager' => $this->storeManagerMock,
+                'quoteCollection' => $this->quoteCollectionMock,
+                'searchResultsDataFactory' => $this->searchResultsDataFactory,
+                'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock
+            ]
+        );
+        $this->model->expects($this->once())->method('getQuoteCollection')->willReturn($this->quoteCollectionMock);
         $this->assertEquals($searchResult, $this->model->getList($searchCriteriaMock));
     }
 
diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php
index 52716b88e8ab602c1848ee3afe2b4a05c889bc2d..f0e265f203ca6c19d35fbe6445a9bf717123c539 100644
--- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php
+++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab;
 
+use Magento\Framework\App\ObjectManager;
+
 class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
     \Magento\Ui\Component\Layout\Tabs\TabInterface
 {
@@ -22,6 +24,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
 
     /**
      * @var \Magento\Config\Model\Config\Source\Yesno
+     * @deprecated
      */
     protected $_sourceYesno;
 
@@ -31,6 +34,13 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
     protected $_nameInLayout = 'actions_apply_to';
 
     /**
+     * @var \Magento\SalesRule\Model\RuleFactory
+     */
+    private $ruleFactory;
+
+    /**
+     * Initialize dependencies.
+     *
      * @param \Magento\Backend\Block\Template\Context $context
      * @param \Magento\Framework\Registry $registry
      * @param \Magento\Framework\Data\FormFactory $formFactory
@@ -54,6 +64,17 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
         parent::__construct($context, $registry, $formFactory, $data);
     }
 
+    /**
+     * @return \Magento\SalesRule\Model\RuleFactory
+     * @deprecated
+     */
+    public function getRuleFactory()
+    {
+        if ($this->ruleFactory instanceof \Magento\SalesRule\Model\RuleFactory) {
+            $this->ruleFactory = ObjectManager::getInstance()->get('\Magento\SalesRule\Model\RuleFactory');
+        }
+    }
+
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -121,24 +142,53 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
      * Prepare form before rendering HTML
      *
      * @return $this
-     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
     protected function _prepareForm()
     {
         $model = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
+        $form = $this->addTabToForm($model);
+        $this->setForm($form);
+
+        return parent::_prepareForm();
+    }
+
+    /**
+     * Handles addition of actions tab to supplied form.
+     *
+     * @param \Magento\SalesRule\Model\Rule $model
+     * @param string $fieldsetId
+     * @param string $formName
+     * @return \Magento\Framework\Data\Form
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    protected function addTabToForm($model, $fieldsetId = 'actions_fieldset', $formName = 'sales_rule_form')
+    {
+        if (!$model) {
+            $id = $this->getRequest()->getParam('id');
+            $model = $this->ruleFactory->create();
+            $model->load($id);
+        }
+
+        $actionsFieldSetId = $model->getActionsFieldSetId($formName);
+
+        $newChildUrl = $this->getUrl(
+            'sales_rule/promo_quote/newActionHtml/form/rule_actions_fieldset_' . $actionsFieldSetId,
+            ['form_namespace' => $formName]
+        );
 
         /** @var \Magento\Framework\Data\Form $form */
         $form = $this->_formFactory->create();
         $form->setHtmlIdPrefix('rule_');
-
         $renderer = $this->_rendererFieldset->setTemplate(
             'Magento_CatalogRule::promo/fieldset.phtml'
         )->setNewChildUrl(
-            $this->getUrl('sales_rule/promo_quote/newActionHtml/form/rule_actions_fieldset')
+            $newChildUrl
+        )->setFieldSetId(
+            $actionsFieldSetId
         );
 
         $fieldset = $form->addFieldset(
-            'actions_fieldset',
+            $fieldsetId,
             [
                 'legend' => __(
                     'Apply the rule only to cart items matching the following conditions ' .
@@ -157,7 +207,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
                 'label' => __('Apply To'),
                 'title' => __('Apply To'),
                 'required' => true,
-                'data-form-part' => 'sales_rule_form'
+                'data-form-part' => $formName
             ]
         )->setRule(
             $model
@@ -168,6 +218,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
         $this->_eventManager->dispatch('adminhtml_block_salesrule_actions_prepareform', ['form' => $form]);
 
         $form->setValues($model->getData());
+        $this->setActionFormName($model->getActions(), $formName);
 
         if ($model->isReadonly()) {
             foreach ($fieldset->getElements() as $element) {
@@ -175,7 +226,23 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
             }
         }
 
-        $this->setForm($form);
-        return parent::_prepareForm();
+        return $form;
+    }
+
+    /**
+     * Handles addition of form name to action and its actions.
+     *
+     * @param \Magento\Rule\Model\Condition\AbstractCondition $actions
+     * @param string $formName
+     * @return void
+     */
+    private function setActionFormName(\Magento\Rule\Model\Condition\AbstractCondition $actions, $formName)
+    {
+        $actions->setFormName($formName);
+        if ($actions->getActions() && is_array($actions->getActions())) {
+            foreach ($actions->getActions() as $condition) {
+                $this->setActionFormName($condition, $formName);
+            }
+        }
     }
 }
diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php
index 0e1d292b2958310977a029f17f0da63df6e0ea5c..7aed719f41b329f6bbf5d327ae44edb949a5d9b0 100644
--- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php
+++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab;
 
+use Magento\Framework\App\ObjectManager;
+
 class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
     \Magento\Ui\Component\Layout\Tabs\TabInterface
 {
@@ -26,6 +28,13 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
     protected $_nameInLayout = 'conditions_apply_to';
 
     /**
+     * @var \Magento\SalesRule\Model\RuleFactory
+     */
+    private $ruleFactory;
+
+    /**
+     * Initialize dependencies.
+     *
      * @param \Magento\Backend\Block\Template\Context $context
      * @param \Magento\Framework\Registry $registry
      * @param \Magento\Framework\Data\FormFactory $formFactory
@@ -46,6 +55,17 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
         parent::__construct($context, $registry, $formFactory, $data);
     }
 
+    /**
+     * @return \Magento\SalesRule\Model\RuleFactory
+     * @deprecated
+     */
+    public function getRuleFactory()
+    {
+        if ($this->ruleFactory instanceof \Magento\SalesRule\Model\RuleFactory) {
+            $this->ruleFactory = ObjectManager::getInstance()->get('\Magento\SalesRule\Model\RuleFactory');
+        }
+    }
+
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -117,19 +137,47 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
     protected function _prepareForm()
     {
         $model = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
+        $form = $this->addTabToForm($model);
+        $this->setForm($form);
+
+        return parent::_prepareForm();
+    }
+
+    /**
+     * Handles addition of conditions tab to supplied form.
+     *
+     * @param \Magento\SalesRule\Model\Rule $model
+     * @param string $fieldsetId
+     * @param string $formName
+     * @return \Magento\Framework\Data\Form
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    protected function addTabToForm($model, $fieldsetId = 'conditions_fieldset', $formName = 'sales_rule_form')
+    {
+        if (!$model) {
+            $id = $this->getRequest()->getParam('id');
+            $model = $this->ruleFactory->create();
+            $model->load($id);
+        }
+        $conditionsFieldSetId = $model->getConditionsFieldSetId($formName);
+        $newChildUrl = $this->getUrl(
+            'sales_rule/promo_quote/newConditionHtml/form/' . $conditionsFieldSetId,
+            ['form_namespace' => $formName]
+        );
 
         /** @var \Magento\Framework\Data\Form $form */
         $form = $this->_formFactory->create();
         $form->setHtmlIdPrefix('rule_');
-
         $renderer = $this->_rendererFieldset->setTemplate(
             'Magento_CatalogRule::promo/fieldset.phtml'
         )->setNewChildUrl(
-            $this->getUrl('sales_rule/promo_quote/newConditionHtml/form/rule_conditions_fieldset')
+            $newChildUrl
+        )->setFieldSetId(
+            $conditionsFieldSetId
         );
 
         $fieldset = $form->addFieldset(
-            'conditions_fieldset',
+            $fieldsetId,
             [
                 'legend' => __(
                     'Apply the rule only if the following conditions are met (leave blank for all products).'
@@ -138,16 +186,15 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
         )->setRenderer(
             $renderer
         );
-
         $fieldset->addField(
             'conditions',
             'text',
             [
-                'name' => 'conditions',
-                'label' => __('Conditions'),
-                'title' => __('Conditions'),
-                'required' => true,
-                'data-form-part' => 'sales_rule_form'
+                'name'           => 'conditions',
+                'label'          => __('Conditions'),
+                'title'          => __('Conditions'),
+                'required'       => true,
+                'data-form-part' => $formName
             ]
         )->setRule(
             $model
@@ -156,8 +203,24 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements
         );
 
         $form->setValues($model->getData());
-        $this->setForm($form);
+        $this->setConditionFormName($model->getConditions(), $formName);
+        return $form;
+    }
 
-        return parent::_prepareForm();
+    /**
+     * Handles addition of form name to condition and its conditions.
+     *
+     * @param \Magento\Rule\Model\Condition\AbstractCondition $conditions
+     * @param string $formName
+     * @return void
+     */
+    private function setConditionFormName(\Magento\Rule\Model\Condition\AbstractCondition $conditions, $formName)
+    {
+        $conditions->setFormName($formName);
+        if ($conditions->getConditions() && is_array($conditions->getConditions())) {
+            foreach ($conditions->getConditions() as $condition) {
+                $this->setConditionFormName($condition, $formName);
+            }
+        }
     }
 }
diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php
index 1e9ae7645d574b8c9b3a396bab0821179a8d27b2..213db3a3c98e56c65eb9aa560c571b1424a260b0 100644
--- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php
+++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php
@@ -8,6 +8,31 @@ namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab;
 class Labels extends \Magento\Backend\Block\Widget\Form\Generic implements
     \Magento\Ui\Component\Layout\Tabs\TabInterface
 {
+    /**
+     * @var \Magento\SalesRule\Model\RuleFactory
+     */
+    private $ruleFactory;
+
+    /**
+     * Initialize dependencies.
+     *
+     * @param \Magento\Backend\Block\Template\Context $context
+     * @param \Magento\Framework\Registry $registry
+     * @param \Magento\Framework\Data\FormFactory $formFactory
+     * @param \Magento\SalesRule\Model\RuleFactory $ruleFactory
+     * @param array $data
+     */
+    public function __construct(
+        \Magento\Backend\Block\Template\Context $context,
+        \Magento\Framework\Registry $registry,
+        \Magento\Framework\Data\FormFactory $formFactory,
+        \Magento\SalesRule\Model\RuleFactory $ruleFactory,
+        array $data = []
+    ) {
+        $this->ruleFactory = $ruleFactory;
+        parent::__construct($context, $registry, $formFactory, $data);
+    }
+
     /**
      * @var string
      */
@@ -83,7 +108,13 @@ class Labels extends \Magento\Backend\Block\Widget\Form\Generic implements
      */
     protected function _prepareForm()
     {
-        $rule = $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
+        $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
+
+        if (!$rule) {
+            $id = $this->getRequest()->getParam('id');
+            $rule = $this->ruleFactory->create();
+            $rule->load($id);
+        }
 
         /** @var \Magento\Framework\Data\Form $form */
         $form = $this->_formFactory->create();
diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php
index e1cb1f5a19a4f9a2f10ed48211fae21a038b18e4..57f45b87a9bd1e647baffa934942a25e133c4ca6 100644
--- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php
+++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php
@@ -65,10 +65,14 @@ class Edit extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote
             $model->addData($data);
         }
 
-        $model->getConditions()->setJsFormObject('rule_conditions_fieldset');
         $model->getConditions()->setFormName('sales_rule_form');
-        $model->getActions()->setJsFormObject('rule_actions_fieldset');
+        $model->getConditions()->setJsFormObject(
+            $model->getConditionsFieldSetId($model->getConditions()->getFormName())
+        );
         $model->getActions()->setFormName('sales_rule_form');
+        $model->getActions()->setJsFormObject(
+            $model->getActionsFieldSetId($model->getActions()->getFormName())
+        );
 
         $this->_initAction();
 
diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php
index cf383bfed2d0c28b46e728a2bdc40425458018f3..061890e406ddf70a98dedc57babda6efbe195c0c 100644
--- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php
+++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php
@@ -16,6 +16,7 @@ class NewActionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote
     public function execute()
     {
         $id = $this->getRequest()->getParam('id');
+        $formName = $this->getRequest()->getParam('form_namespace');
         $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type')));
         $type = $typeArr[0];
 
@@ -35,7 +36,7 @@ class NewActionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote
         }
 
         if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) {
-            $model->setJsFormObject($this->getRequest()->getParam('form'));
+            $model->setJsFormObject($formName);
             $html = $model->asHtmlRecursive();
         } else {
             $html = '';
diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php
index fb0c502a37e1a72734f9d26507ad47ec16e46bb3..dd71d81f871775bed8beba332ad8ff4b89fb020e 100644
--- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php
+++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php
@@ -16,6 +16,7 @@ class NewConditionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quo
     public function execute()
     {
         $id = $this->getRequest()->getParam('id');
+        $formName = $this->getRequest()->getParam('form_namespace');
         $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type')));
         $type = $typeArr[0];
 
@@ -36,6 +37,7 @@ class NewConditionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quo
 
         if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) {
             $model->setJsFormObject($this->getRequest()->getParam('form'));
+            $model->setFormName($formName);
             $html = $model->asHtmlRecursive();
         } else {
             $html = '';
diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php
index fea1326e605307bcadf9ba892e0baa69d35f3345..122bee0b2c38abed6ed57b149e82eb3355b5c85b 100644
--- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php
+++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php
@@ -403,4 +403,30 @@ class Rule extends AbstractResource
         }
         return $this;
     }
+
+    /**
+     * Delete the object
+     *
+     * @param \Magento\Framework\Model\AbstractModel $object
+     * @return $this
+     * @throws \Exception
+     */
+    public function delete(AbstractModel $object)
+    {
+        $this->transactionManager->start($this->getConnection());
+        try {
+            $object->beforeDelete();
+            $this->_beforeDelete($object);
+            $this->entityManager->delete('Magento\SalesRule\Api\Data\RuleInterface', $object);
+            $this->_afterDelete($object);
+            $object->isDeleted(true);
+            $object->afterDelete();
+            $this->transactionManager->commit();
+            $object->afterDeleteCommit();
+        } catch (\Exception $exception) {
+            $this->transactionManager->rollBack();
+            throw $exception;
+        }
+        return $this;
+    }
 }
diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php
index 209ba4be22a40eb52fff869712d23a01033fddd9..bd9f6872d1f912923bdfcbf8876a40757a6f95e1 100644
--- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php
+++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php
@@ -11,9 +11,8 @@ namespace Magento\SalesRule\Model\ResourceModel\Rule;
 use Magento\Quote\Model\Quote\Address;
 
 /**
- * Sales Rules resource collection model
- *
- * @author      Magento Core Team <core@magentocommerce.com>
+ * Sales Rules resource collection model.
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection
 {
@@ -73,6 +72,52 @@ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\Abstr
         $this->_map['fields']['rule_id'] = 'main_table.rule_id';
     }
 
+    /**
+     * @param string $entityType
+     * @param string $objectField
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    protected function mapAssociatedEntities($entityType, $objectField)
+    {
+        if (!$this->_items) {
+            return;
+        }
+
+        $entityInfo = $this->_getAssociatedEntityInfo($entityType);
+        $ruleIdField = $entityInfo['rule_id_field'];
+        $entityIds = $this->getColumnValues($ruleIdField);
+
+        $select = $this->getConnection()->select()->from(
+            $this->getTable($entityInfo['associations_table'])
+        )->where(
+            $ruleIdField . ' IN (?)',
+            $entityIds
+        );
+
+        $associatedEntities = $this->getConnection()->fetchAll($select);
+
+        array_map(function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) {
+            $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]);
+            $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField);
+            $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']];
+            $item->setData($objectField, $itemAssociatedValue);
+        }, $associatedEntities);
+    }
+
+    /**
+     * @return $this
+     * @throws \Exception
+     */
+    protected function _afterLoad()
+    {
+        $this->mapAssociatedEntities('website', 'website_ids');
+        $this->mapAssociatedEntities('customer_group', 'customer_group_ids');
+
+        $this->setFlag('add_websites_to_result', false);
+        return parent::_afterLoad();
+    }
+
     /**
      * Filter collection by specified website, customer group, coupon code, date.
      * Filter collection to use only active rules.
@@ -253,4 +298,27 @@ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\Abstr
 
         return $this;
     }
+
+    /**
+     * Limit rules collection by specific customer group
+     *
+     * @param int $customerGroupId
+     * @return $this
+     */
+    public function addCustomerGroupFilter($customerGroupId)
+    {
+        $entityInfo = $this->_getAssociatedEntityInfo('customer_group');
+        if (!$this->getFlag('is_customer_group_joined')) {
+            $this->setFlag('is_customer_group_joined', true);
+            $this->getSelect()->join(
+                ['customer_group' => $this->getTable($entityInfo['associations_table'])],
+                $this->getConnection()
+                    ->quoteInto('customer_group.' . $entityInfo['entity_id_field'] . ' = ?', $customerGroupId)
+                . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = customer_group.'
+                . $entityInfo['rule_id_field'],
+                []
+            );
+        }
+        return $this;
+    }
 }
diff --git a/app/code/Magento/SalesRule/Model/Rule.php b/app/code/Magento/SalesRule/Model/Rule.php
index a171769505db56b20f1999228ff70d69c994486b..6a6382bb938ee584b56d813e5d3dbb6915ab96ac 100644
--- a/app/code/Magento/SalesRule/Model/Rule.php
+++ b/app/code/Magento/SalesRule/Model/Rule.php
@@ -598,4 +598,22 @@ class Rule extends \Magento\Rule\Model\AbstractModel
         }
         return $address;
     }
+
+    /**
+     * @param string $formName
+     * @return string
+     */
+    public function getConditionsFieldSetId($formName = '')
+    {
+        return $formName . 'rule_conditions_fieldset_' . $this->getId();
+    }
+
+    /**
+     * @param string $formName
+     * @return string
+     */
+    public function getActionsFieldSetId($formName = '')
+    {
+        return $formName . 'rule_actions_fieldset_' . $this->getId();
+    }
 }
diff --git a/app/code/Magento/SalesRule/Model/Rule/DataProvider.php b/app/code/Magento/SalesRule/Model/Rule/DataProvider.php
index 768ec754e0a2fde083f9597bd1b83a137533a714..79bde496876fd735e6d2016d8c6884f9fa6a262e 100644
--- a/app/code/Magento/SalesRule/Model/Rule/DataProvider.php
+++ b/app/code/Magento/SalesRule/Model/Rule/DataProvider.php
@@ -8,14 +8,9 @@ namespace Magento\SalesRule\Model\Rule;
 use Magento\SalesRule\Model\ResourceModel\Rule\Collection;
 use Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory;
 use Magento\SalesRule\Model\Rule;
-use Magento\Store\Model\System\Store;
-use Magento\Customer\Api\GroupRepositoryInterface;
-use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Framework\Convert\DataObject;
 
 /**
  * Class DataProvider
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
 {
@@ -29,31 +24,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
      */
     protected $loadedData;
 
-    /**
-     * @var Store
-     */
-    protected $store;
-
-    /**
-     * @var GroupRepositoryInterface
-     */
-    protected $groupRepository;
-
-    /**
-     * @var SearchCriteriaBuilder
-     */
-    protected $searchCriteriaBuilder;
-
-    /**
-     * @var DataObject
-     */
-    protected $objectConverter;
-
-    /**
-     * @var \Magento\SalesRule\Model\RuleFactory
-     */
-    protected $salesRuleFactory;
-
     /**
      * Core registry
      *
@@ -62,203 +32,52 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
     protected $coreRegistry;
 
     /**
-     * DataProvider constructor.
+     * @var \Magento\SalesRule\Model\Rule\Metadata\ValueProvider
+     */
+    protected $metadataValueProvider;
+
+    /**
+     * Initialize dependencies.
      *
      * @param string $name
      * @param string $primaryFieldName
      * @param string $requestFieldName
      * @param CollectionFactory $collectionFactory
-     * @param Store $store
-     * @param GroupRepositoryInterface $groupRepository
-     * @param SearchCriteriaBuilder $searchCriteriaBuilder
-     * @param DataObject $objectConverter
-     * @param \Magento\SalesRule\Model\RuleFactory $salesRuleFactory
      * @param \Magento\Framework\Registry $registry
+     * @param Metadata\ValueProvider $metadataValueProvider
      * @param array $meta
      * @param array $data
-     *
-     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
         $name,
         $primaryFieldName,
         $requestFieldName,
         CollectionFactory $collectionFactory,
-        Store $store,
-        GroupRepositoryInterface $groupRepository,
-        SearchCriteriaBuilder $searchCriteriaBuilder,
-        DataObject $objectConverter,
-        \Magento\SalesRule\Model\RuleFactory $salesRuleFactory,
         \Magento\Framework\Registry $registry,
+        \Magento\SalesRule\Model\Rule\Metadata\ValueProvider $metadataValueProvider,
         array $meta = [],
         array $data = []
     ) {
-        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
         $this->collection = $collectionFactory->create();
-        $this->store = $store;
-        $this->groupRepository = $groupRepository;
-        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
-        $this->objectConverter = $objectConverter;
-        $this->salesRuleFactory = $salesRuleFactory;
         $this->coreRegistry = $registry;
-        $this->initMeta();
+        $this->metadataValueProvider = $metadataValueProvider;
+        $meta = array_replace_recursive($this->getMetadataValues(), $meta);
+        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
     }
 
     /**
-     * @return void
-     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     * Get metadata values
+     *
+     * @return array
      */
-    protected function initMeta()
+    protected function getMetadataValues()
     {
-        $customerGroups = $this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems();
-        $applyOptions = [
-            ['label' => __('Percent of product price discount'), 'value' =>  Rule::BY_PERCENT_ACTION],
-            ['label' => __('Fixed amount discount'), 'value' => Rule::BY_FIXED_ACTION],
-            ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::CART_FIXED_ACTION],
-            ['label' => __('Buy X get Y free (discount amount is Y)'), 'value' => Rule::BUY_X_GET_Y_ACTION]
-        ];
-
-        $couponTypesOptions = [];
-        $couponTypes = $this->salesRuleFactory->create()->getCouponTypes();
-        foreach ($couponTypes as $key => $couponType) {
-            $couponTypesOptions[] = [
-                'label' => $couponType,
-                'value' => $key,
-            ];
-        }
-
         $rule = $this->coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
-        $labels = $rule->getStoreLabels();
-
-        $this->meta = [
-            'rule_information' => [
-                'children' => [
-                    'website_ids' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => $this->store->getWebsiteValuesForForm(),
-                                ],
-                            ],
-                        ],
-                    ],
-                    'is_active' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => [
-                                        ['label' => __('Active'), 'value' => '1'],
-                                        ['label' => __('Inactive'), 'value' => '0']
-                                    ],
-                                ],
-                            ],
-                        ],
-                    ],
-                    'customer_group_ids' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => $this->objectConverter->toOptionArray($customerGroups, 'id', 'code'),
-                                ],
-                            ],
-                        ],
-                    ],
-                    'coupon_type' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => $couponTypesOptions,
-                                ],
-                            ],
-                        ],
-                    ],
-                    'is_rss' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => [
-                                        ['label' => __('Yes'), 'value' => '1'],
-                                        ['label' => __('No'), 'value' => '0']
-                                    ],
-                                ],
-                            ],
-                        ],
-                    ],
-                ]
-            ],
-            'actions' => [
-                'children' => [
-                    'simple_action' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => $applyOptions
-                                ],
-                            ]
-                        ]
-                    ],
-                    'discount_amount' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'value' => '0',
-                                ],
-                            ],
-                        ],
-                    ],
-                    'discount_qty' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'value' => '0',
-                                ],
-                            ],
-                        ],
-                    ],
-                    'apply_to_shipping' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => [
-                                        ['label' => __('Yes'), 'value' => '1'],
-                                        ['label' => __('No'), 'value' => '0']
-                                    ]
-                                ],
-                            ],
-                        ],
-                    ],
-                    'stop_rules_processing' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'options' => [
-                                        ['label' => __('Yes'), 'value' => '1'],
-                                        ['label' => __('No'), 'value' => '0'],
-                                    ],
-                                ],
-                            ]
-                        ]
-                    ],
-                ]
-            ],
-            'labels' => [
-                'children' => [
-                    'store_labels[0]' => [
-                        'arguments' => [
-                            'data' => [
-                                'config' => [
-                                    'value' => isset($labels[0]) ? $labels[0] : '',
-                                ],
-                            ]
-                        ]
-                    ]
-                ]
-            ],
-        ];
+        return $this->metadataValueProvider->getMetadataValues($rule);
     }
 
     /**
-     * @return array
+     * {@inheritdoc}
      */
     public function getData()
     {
diff --git a/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2845b5522ec292c46c649a682bc5105b8b684d5
--- /dev/null
+++ b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\SalesRule\Model\Rule\Metadata;
+
+use Magento\SalesRule\Model\ResourceModel\Rule\Collection;
+use Magento\SalesRule\Model\Rule;
+use Magento\Store\Model\System\Store;
+use Magento\Customer\Api\GroupRepositoryInterface;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\Convert\DataObject;
+
+/**
+ * Metadata provider for sales rule edit form.
+ */
+class ValueProvider
+{
+    /**
+     * @var Store
+     */
+    protected $store;
+
+    /**
+     * @var GroupRepositoryInterface
+     */
+    protected $groupRepository;
+
+    /**
+     * @var SearchCriteriaBuilder
+     */
+    protected $searchCriteriaBuilder;
+
+    /**
+     * @var DataObject
+     */
+    protected $objectConverter;
+
+    /**
+     * @var \Magento\SalesRule\Model\RuleFactory
+     */
+    protected $salesRuleFactory;
+
+    /**
+     * Initialize dependencies.
+     *
+     * @param Store $store
+     * @param GroupRepositoryInterface $groupRepository
+     * @param SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param DataObject $objectConverter
+     * @param \Magento\SalesRule\Model\RuleFactory $salesRuleFactory
+     */
+    public function __construct(
+        Store $store,
+        GroupRepositoryInterface $groupRepository,
+        SearchCriteriaBuilder $searchCriteriaBuilder,
+        DataObject $objectConverter,
+        \Magento\SalesRule\Model\RuleFactory $salesRuleFactory
+    ) {
+        $this->store = $store;
+        $this->groupRepository = $groupRepository;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->objectConverter = $objectConverter;
+        $this->salesRuleFactory = $salesRuleFactory;
+    }
+
+    /**
+     * Get metadata for sales rule form. It will be merged with form UI component declaration.
+     *
+     * @param Rule $rule
+     * @return array
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function getMetadataValues(\Magento\SalesRule\Model\Rule $rule)
+    {
+        $customerGroups = $this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems();
+        $applyOptions = [
+            ['label' => __('Percent of product price discount'), 'value' =>  Rule::BY_PERCENT_ACTION],
+            ['label' => __('Fixed amount discount'), 'value' => Rule::BY_FIXED_ACTION],
+            ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::BY_PERCENT_ACTION],
+            ['label' => __('Buy X get Y free (discount amount is Y)'), 'value' => Rule::BUY_X_GET_Y_ACTION]
+        ];
+
+        $couponTypesOptions = [];
+        $couponTypes = $this->salesRuleFactory->create()->getCouponTypes();
+        foreach ($couponTypes as $key => $couponType) {
+            $couponTypesOptions[] = [
+                'label' => $couponType,
+                'value' => $key,
+            ];
+        }
+
+        $labels = $rule->getStoreLabels();
+
+        return [
+            'rule_information' => [
+                'children' => [
+                    'website_ids' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => $this->store->getWebsiteValuesForForm(),
+                                ],
+                            ],
+                        ],
+                    ],
+                    'is_active' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => [
+                                        ['label' => __('Active'), 'value' => '1'],
+                                        ['label' => __('Inactive'), 'value' => '0']
+                                    ],
+                                ],
+                            ],
+                        ],
+                    ],
+                    'customer_group_ids' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => $this->objectConverter->toOptionArray($customerGroups, 'id', 'code'),
+                                ],
+                            ],
+                        ],
+                    ],
+                    'coupon_type' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => $couponTypesOptions,
+                                ],
+                            ],
+                        ],
+                    ],
+                    'is_rss' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => [
+                                        ['label' => __('Yes'), 'value' => '1'],
+                                        ['label' => __('No'), 'value' => '0']
+                                    ],
+                                ],
+                            ],
+                        ],
+                    ],
+                ]
+            ],
+            'actions' => [
+                'children' => [
+                    'simple_action' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => $applyOptions
+                                ],
+                            ]
+                        ]
+                    ],
+                    'discount_amount' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'value' => '0',
+                                ],
+                            ],
+                        ],
+                    ],
+                    'discount_qty' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'value' => '0',
+                                ],
+                            ],
+                        ],
+                    ],
+                    'apply_to_shipping' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => [
+                                        ['label' => __('Yes'), 'value' => '1'],
+                                        ['label' => __('No'), 'value' => '0']
+                                    ]
+                                ],
+                            ],
+                        ],
+                    ],
+                    'stop_rules_processing' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'options' => [
+                                        ['label' => __('Yes'), 'value' => '1'],
+                                        ['label' => __('No'), 'value' => '0'],
+                                    ],
+                                ],
+                            ]
+                        ]
+                    ],
+                ]
+            ],
+            'labels' => [
+                'children' => [
+                    'store_labels[0]' => [
+                        'arguments' => [
+                            'data' => [
+                                'config' => [
+                                    'value' => isset($labels[0]) ? $labels[0] : '',
+                                ],
+                            ]
+                        ]
+                    ]
+                ]
+            ],
+        ];
+    }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php
index a3a74f10ecb26295767a61cab45f7ec5cfdfd638..dc09ba26f904842fa531cf94e039f0d2253002a3 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php
@@ -80,7 +80,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase
             ->willReturn($this->resourcesMock);
 
         $this->entityManager = $this->getMockBuilder('Magento\Framework\Model\EntityManager')
-            ->setMethods(['load', 'save'])
+            ->setMethods(['load', 'save', 'delete'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -125,7 +125,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase
                 'context' => $context,
                 'connectionName' => $connectionName,
                 'associatedEntitiesMap' => $associatedEntitiesMap,
-                'entityManager' => $this->entityManager
+                'entityManager' => $this->entityManager,
             ]
         );
     }
@@ -310,4 +310,38 @@ class RuleTest extends \PHPUnit_Framework_TestCase
 
         return $abstractModelMock;
     }
+
+    public function testDeleteSuccess()
+    {
+        $this->transactionManagerMock->expects($this->once())->method('start');
+        $abstractModelMock = $this->getMockBuilder('Magento\Rule\Model\AbstractModel')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+        $abstractModelMock->expects($this->once())->method('beforeDelete');
+        $this->entityManager->expects($this->once())->method('delete');
+        $abstractModelMock->expects($this->once())->method('isDeleted');
+        $abstractModelMock->expects($this->once())->method('afterDelete');
+        $this->transactionManagerMock->expects($this->once())->method('commit');
+
+        $this->model->delete($abstractModelMock);
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage To test exception
+     */
+    public function testDeletionRollbackOnFailure()
+    {
+        $expectedException = new \Exception(__('To test exception'));
+        $this->transactionManagerMock->expects($this->once())->method('start');
+        $abstractModelMock = $this->getMockBuilder('Magento\Rule\Model\AbstractModel')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+        $abstractModelMock->expects($this->once())->method('beforeDelete')->willThrowException($expectedException);
+        $this->transactionManagerMock->expects($this->once())->method('rollBack');
+
+        $this->model->delete($abstractModelMock);
+    }
 }
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php
index 385443fc5ef38978e68b5d859325f4bd00f80186..aeb0508c8f3b63156c978ab695b9ee2ce50e0415 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php
@@ -53,16 +53,6 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->searchCriteriaBuilderMock = $this->getMock(
-            'Magento\Framework\Api\SearchCriteriaBuilder',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->storeMock = $this->getMock('Magento\Store\Model\System\Store', [], [], '', false);
-        $this->groupRepositoryMock = $this->getMock('Magento\Customer\Api\GroupRepositoryInterface', [], [], '', false);
-        $this->dataObjectMock = $this->getMock('Magento\Framework\Convert\DataObject', [], [], '', false);
 
         $this->collectionMock = $this->getMock(
             'Magento\SalesRule\Model\ResourceModel\Rule\Collection',
@@ -72,48 +62,16 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock);
-        $searchCriteriaMock = $this->getMock('Magento\Framework\Api\SearchCriteriaInterface', [], [], '', false);
-        $groupSearchResultsMock = $this->getMock(
-            'Magento\Customer\Api\Data\GroupSearchResultsInterface',
-            [],
-            [],
-            '',
-            false
-        );
-        $groupsMock = $this->getMock('Magento\Customer\Api\Data\GroupInterface', [], [], '', false);
-
-        $this->searchCriteriaBuilderMock->expects($this->once())->method('create')->willReturn($searchCriteriaMock);
-        $this->groupRepositoryMock->expects($this->once())->method('getList')->with($searchCriteriaMock)
-            ->willReturn($groupSearchResultsMock);
-        $groupSearchResultsMock->expects($this->once())->method('getItems')->willReturn([$groupsMock]);
-        $this->storeMock->expects($this->once())->method('getWebsiteValuesForForm')->willReturn([]);
-        $this->dataObjectMock->expects($this->once())->method('toOptionArray')->with([$groupsMock], 'id', 'code')
-            ->willReturn([]);
-        $ruleFactoryMock = $this->getMock('Magento\SalesRule\Model\RuleFactory', ['create'], [], '', false);
         $ruleMock = $this->getMock('Magento\SalesRule\Model\Rule', [], [], '', false);
-        $ruleFactoryMock->expects($this->once())
-            ->method('create')
-            ->willReturn($ruleMock);
-        $ruleMock->expects($this->once())
-            ->method('getCouponTypes')
-            ->willReturn(
-                [
-                    'key1' => 'couponType1',
-                    'key2' => 'couponType2',
-                ]
-            );
+        $metaDataValueProviderMock = $this->getMockBuilder('Magento\SalesRule\Model\Rule\Metadata\ValueProvider')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
         $registryMock = $this->getMock('Magento\Framework\Registry', [], [], '', false);
         $registryMock->expects($this->once())
             ->method('registry')
             ->willReturn($ruleMock);
-        $ruleMock->expects($this->once())
-            ->method('getStoreLabels')
-            ->willReturn(
-                [
-                    'label0',
-                    'label1',
-                ]
-            );
+        $metaDataValueProviderMock->expects($this->once())->method('getMetadataValues')->willReturn(['data']);
         $this->model = (new ObjectManager($this))->getObject(
             'Magento\SalesRule\Model\Rule\DataProvider',
             [
@@ -121,12 +79,8 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase
                 'primaryFieldName' => 'Primary',
                 'requestFieldName' => 'Request',
                 'collectionFactory' => $this->collectionFactoryMock,
-                'store' => $this->storeMock,
-                'groupRepository' => $this->groupRepositoryMock,
-                'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock,
-                'objectConverter' => $this->dataObjectMock,
-                'salesRuleFactory' => $ruleFactoryMock,
                 'registry' => $registryMock,
+                'metadataValueProvider' => $metaDataValueProviderMock
             ]
         );
     }
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..43f3d5c7143df489abe9bb4d92dbbcfc5be4c8db
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\SalesRule\Test\Unit\Model\Rule\Metadata;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+/**
+ * @covers Magento\SalesRule\Model\Rule\Metadata\ValueProvider
+ */
+class ValueProviderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\SalesRule\Model\Rule\Metadata\ValueProvider
+     */
+    protected $model;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $groupRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $searchCriteriaBuilderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $dataObjectMock;
+
+    /**
+     * @var \Magento\SalesRule\Model\RuleFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $ruleFactoryMock;
+
+    protected function setUp()
+    {
+        $this->searchCriteriaBuilderMock = $this->getMock(
+            'Magento\Framework\Api\SearchCriteriaBuilder',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->storeMock = $this->getMock('Magento\Store\Model\System\Store', [], [], '', false);
+        $this->groupRepositoryMock = $this->getMock('Magento\Customer\Api\GroupRepositoryInterface', [], [], '', false);
+        $this->dataObjectMock = $this->getMock('Magento\Framework\Convert\DataObject', [], [], '', false);
+        $searchCriteriaMock = $this->getMock('Magento\Framework\Api\SearchCriteriaInterface', [], [], '', false);
+        $groupSearchResultsMock = $this->getMock(
+            'Magento\Customer\Api\Data\GroupSearchResultsInterface',
+            [],
+            [],
+            '',
+            false
+        );
+        $groupsMock = $this->getMock('Magento\Customer\Api\Data\GroupInterface', [], [], '', false);
+
+        $this->searchCriteriaBuilderMock->expects($this->once())->method('create')->willReturn($searchCriteriaMock);
+        $this->groupRepositoryMock->expects($this->once())->method('getList')->with($searchCriteriaMock)
+            ->willReturn($groupSearchResultsMock);
+        $groupSearchResultsMock->expects($this->once())->method('getItems')->willReturn([$groupsMock]);
+        $this->storeMock->expects($this->once())->method('getWebsiteValuesForForm')->willReturn([]);
+        $this->dataObjectMock->expects($this->once())->method('toOptionArray')->with([$groupsMock], 'id', 'code')
+            ->willReturn([]);
+        $this->ruleFactoryMock = $this->getMock('Magento\SalesRule\Model\RuleFactory', ['create'], [], '', false);
+        $this->model = (new ObjectManager($this))->getObject(
+            'Magento\SalesRule\Model\Rule\Metadata\ValueProvider',
+            [
+                'store' => $this->storeMock,
+                'groupRepository' => $this->groupRepositoryMock,
+                'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock,
+                'objectConverter' => $this->dataObjectMock,
+                'salesRuleFactory' => $this->ruleFactoryMock,
+            ]
+        );
+    }
+
+    public function testGetMetadataValues()
+    {
+        $expectedData = include __DIR__ . '/_files/MetaData.php';
+
+        /** @var \Magento\SalesRule\Model\Rule|\PHPUnit_Framework_MockObject_MockObject $ruleMock */
+        $ruleMock = $this->getMock('Magento\SalesRule\Model\Rule', [], [], '', false);
+        $this->ruleFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($ruleMock);
+        $ruleMock->expects($this->once())
+            ->method('getCouponTypes')
+            ->willReturn(
+                [
+                    'key1' => 'couponType1',
+                    'key2' => 'couponType2',
+                ]
+            );
+        $ruleMock->expects($this->once())
+            ->method('getStoreLabels')
+            ->willReturn(
+                [
+                    'label0'
+                ]
+            );
+        $test = $this->model->getMetadataValues($ruleMock);
+        $this->assertEquals($expectedData, $test);
+    }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php
new file mode 100644
index 0000000000000000000000000000000000000000..d93c504af9e71c7490c304f6b5fd43e87c335daa
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php
@@ -0,0 +1,170 @@
+<?php
+return [
+    'rule_information' =>
+        [
+            'children' =>
+                [
+                    'website_ids' => ['arguments' => ['data' => ['config' => ['options' => []]]]],
+                    'is_active' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => __('Active'),
+                                                                'value' => '1',
+                                                            ],
+                                                            [
+                                                                'label' => __('Inactive'),
+                                                                'value' => '0',
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                    'customer_group_ids' => ['arguments' => ['data' => ['config' => ['options' => []]]]],
+                    'coupon_type' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => 'couponType1',
+                                                                'value' => 'key1',
+                                                            ],
+                                                            [
+                                                                'label' => 'couponType2',
+                                                                'value' => 'key2',
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                    'is_rss' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => __('Yes'),
+                                                                'value' => '1',
+                                                            ],
+                                                            [
+                                                                'label' => __('No'),
+                                                                'value' => '0',
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                ],
+        ],
+    'actions' =>
+        [
+            'children' =>
+                [
+                    'simple_action' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => __('Percent of product price discount'),
+                                                                'value' => 'by_percent',
+                                                            ],
+                                                            [
+                                                                'label' => __('Fixed amount discount'),
+                                                                'value' => 'by_fixed',
+                                                            ],
+                                                            [
+                                                                'label' => __('Fixed amount discount for whole cart'),
+                                                                'value' => 'by_percent',
+                                                            ],
+                                                            [
+                                                                'label' => __(
+                                                                    'Buy X get Y free (discount amount is Y)'
+                                                                ),
+                                                                'value' => 'buy_x_get_y',
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                    'discount_amount' => ['arguments' => ['data' => ['config' => ['value' => '0']]]],
+                    'discount_qty' => ['arguments' => ['data' => ['config' => ['value' => '0']]]],
+                    'apply_to_shipping' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => __('Yes'),
+                                                                'value' => '1'
+                                                            ],
+                                                            [
+                                                                'label' => __('No'),
+                                                                'value' => '0'
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                    'stop_rules_processing' =>
+                        [
+                            'arguments' =>
+                                [
+                                    'data' =>
+                                        [
+                                            'config' =>
+                                                [
+
+                                                    'options' =>
+                                                        [
+                                                            [
+                                                                'label' => __('Yes'),
+                                                                'value' => '1'
+                                                            ],
+
+                                                            [
+                                                                'label' => __('No'),
+                                                                'value' => '0'
+                                                            ],
+                                                        ],
+                                                ],
+                                        ],
+                                ],
+                        ],
+                ],
+        ],
+    'labels' => ['children' => ['store_labels[0]' => ['arguments' => ['data' => ['config' => ['value' => 'label0']]]]]],
+];
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php
index 65243ee594e6e9be3a796a2c252dd1c69db7065f..2a007d775bcdb8ac19f739555aafd7ae5acc21ce 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php
@@ -48,11 +48,13 @@ class RuleTest extends \PHPUnit_Framework_TestCase
         $this->conditionCombineFactoryMock = $this->getMockBuilder(
             '\Magento\SalesRule\Model\Rule\Condition\CombineFactory'
         )->disableOriginalConstructor()
+            ->setMethods(['create'])
             ->getMock();
 
         $this->condProdCombineFactoryMock = $this->getMockBuilder(
             '\Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory'
         )->disableOriginalConstructor()
+            ->setMethods(['create'])
             ->getMock();
 
         $this->model = $objectManager->getObject(
@@ -150,4 +152,20 @@ class RuleTest extends \PHPUnit_Framework_TestCase
 
         return $conditionMock;
     }
+
+    public function testGetConditionsFieldSetId()
+    {
+        $formName = 'form_name';
+        $this->model->setId(100);
+        $expectedResult = 'form_namerule_conditions_fieldset_100';
+        $this->assertEquals($expectedResult, $this->model->getConditionsFieldSetId($formName));
+    }
+
+    public function testGetActionsFieldSetId()
+    {
+        $formName = 'form_name';
+        $this->model->setId(100);
+        $expectedResult = 'form_namerule_actions_fieldset_100';
+        $this->assertEquals($expectedResult, $this->model->getActionsFieldSetId($formName));
+    }
 }
diff --git a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml
index 241c802efb0874fd3ce31e0345a1e291845a8a06..0a2ff4744720cec14c7669250ae01861b8747c0d 100644
--- a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml
+++ b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml
@@ -48,12 +48,21 @@
             </item>
         </argument>
     </dataSource>
+    <fieldset name="general">
+        <argument name="data" xsi:type="array">
+            <item name="config" xsi:type="array">
+                <item name="label" xsi:type="string">Currently Active</item>
+                <item name="additionalClasses" xsi:type="string">fieldset-schedule</item>
+            </item>
+        </argument>
+    </fieldset>
     <fieldset name="rule_information">
         <argument name="data" xsi:type="array">
             <item name="config" xsi:type="array">
                 <item name="label" xsi:type="string" translate="true">Rule Information</item>
                 <item name="collapsible" xsi:type="boolean">true</item>
                 <item name="sortOrder" xsi:type="number">10</item>
+                <item name="opened" xsi:type="boolean">true</item>
             </item>
         </argument>
         <field name="name">
@@ -86,15 +95,19 @@
         <field name="is_active">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
-                    <item name="label" xsi:type="string" translate="true">Status</item>
-                    <item name="visible" xsi:type="boolean">true</item>
-                    <item name="dataType" xsi:type="string">number</item>
-                    <item name="formElement" xsi:type="string">select</item>
+                    <item name="dataType" xsi:type="string">boolean</item>
+                    <item name="formElement" xsi:type="string">checkbox</item>
                     <item name="source" xsi:type="string">sales_rule</item>
-                    <item name="dataScope" xsi:type="string">is_active</item>
+                    <item name="prefer" xsi:type="string">toggle</item>
+                    <item name="valueMap" xsi:type="array">
+                        <item name="true" xsi:type="number">1</item>
+                        <item name="false" xsi:type="number">0</item>
+                    </item>
                     <item name="validation" xsi:type="array">
                         <item name="required-entry" xsi:type="boolean">true</item>
                     </item>
+                    <item name="default" xsi:type="number">1</item>
+                    <item name="label" xsi:type="string" translate="true">Active</item>
                 </item>
             </argument>
         </field>
@@ -328,12 +341,16 @@
         <field name="is_rss">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
-                    <item name="label" xsi:type="string" translate="true">Public In RSS Feed</item>
-                    <item name="visible" xsi:type="boolean">true</item>
-                    <item name="dataType" xsi:type="string">number</item>
-                    <item name="formElement" xsi:type="string">select</item>
+                    <item name="dataType" xsi:type="string">boolean</item>
+                    <item name="formElement" xsi:type="string">checkbox</item>
                     <item name="source" xsi:type="string">sales_rule</item>
-                    <item name="dataScope" xsi:type="string">is_rss</item>
+                    <item name="prefer" xsi:type="string">toggle</item>
+                    <item name="valueMap" xsi:type="array">
+                        <item name="true" xsi:type="number">1</item>
+                        <item name="false" xsi:type="number">0</item>
+                    </item>
+                    <item name="default" xsi:type="number">1</item>
+                    <item name="label" xsi:type="string" translate="true">Public In RSS Feed</item>
                 </item>
             </argument>
         </field>
@@ -417,26 +434,32 @@
         <field name="apply_to_shipping">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
-                    <item name="label" xsi:type="string" translate="true">Apply to Shipping Amount</item>
-                    <item name="fieldGroup" xsi:type="string">bool</item>
-                    <item name="dataType" xsi:type="string">number</item>
-                    <item name="formElement" xsi:type="string">select</item>
-                    <item name="value" xsi:type="string">0</item>
+                    <item name="dataType" xsi:type="string">boolean</item>
+                    <item name="formElement" xsi:type="string">checkbox</item>
                     <item name="source" xsi:type="string">sales_rule</item>
-                    <item name="dataScope" xsi:type="string">apply_to_shipping</item>
+                    <item name="prefer" xsi:type="string">toggle</item>
+                    <item name="valueMap" xsi:type="array">
+                        <item name="true" xsi:type="number">1</item>
+                        <item name="false" xsi:type="number">0</item>
+                    </item>
+                    <item name="default" xsi:type="number">0</item>
+                    <item name="label" xsi:type="string" translate="true">Apply to Shipping Amount</item>
                 </item>
             </argument>
         </field>
         <field name="stop_rules_processing">
             <argument name="data" xsi:type="array">
                 <item name="config" xsi:type="array">
-                    <item name="label" xsi:type="string" translate="true">Discard subsequent rules</item>
-                    <item name="fieldGroup" xsi:type="string">bool</item>
-                    <item name="dataType" xsi:type="string">number</item>
-                    <item name="formElement" xsi:type="string">select</item>
-                    <item name="value" xsi:type="string">0</item>
+                    <item name="dataType" xsi:type="string">boolean</item>
+                    <item name="formElement" xsi:type="string">checkbox</item>
                     <item name="source" xsi:type="string">sales_rule</item>
-                    <item name="dataScope" xsi:type="string">stop_rules_processing</item>
+                    <item name="prefer" xsi:type="string">toggle</item>
+                    <item name="valueMap" xsi:type="array">
+                        <item name="true" xsi:type="number">1</item>
+                        <item name="false" xsi:type="number">0</item>
+                    </item>
+                    <item name="default" xsi:type="number">0</item>
+                    <item name="label" xsi:type="string" translate="true">Discard subsequent rules</item>
                 </item>
             </argument>
         </field>
diff --git a/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php b/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php
new file mode 100644
index 0000000000000000000000000000000000000000..018403de69351d0bfbfba597e068f1d825e0e840
--- /dev/null
+++ b/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Adapter\Mysql\Query\Preprocessor;
+
+use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface;
+use Magento\Search\Api\SynonymAnalyzerInterface;
+
+class Synonyms implements PreprocessorInterface
+{
+    /**
+     * @var SynonymAnalyzerInterface
+     */
+    private $synonymsAnalyzer;
+
+    /**
+     * Constructor
+     * 
+     * @param SynonymAnalyzerInterface $synonymsAnalyzer
+     */
+    public function __construct(SynonymAnalyzerInterface $synonymsAnalyzer)
+    {
+        $this->synonymsAnalyzer = $synonymsAnalyzer;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process($query)
+    {
+        $synonyms = [];
+        $synonymsArray = $this->synonymsAnalyzer->getSynonymsForPhrase($query);
+        if (count($synonymsArray) > 0) {
+            foreach ($synonymsArray as $synonymPart) {
+                $synonyms [] = implode(' ', $synonymPart);
+            }
+            $query = implode(' ', $synonyms);
+        }
+        return $query;
+    }
+}
diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php
index 82e862ba5cbdfb8c2be8c65360926d89c434fde7..fd28901dc566c116bc630efd5e4c2d7f798b6792 100644
--- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php
+++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php
@@ -91,7 +91,7 @@ class Top extends \Magento\Backend\Block\Dashboard\Grid
             [
                 'header' => __('Search Term'),
                 'sortable' => false,
-                'index' => 'name',
+                'index' => 'query_text',
                 'renderer' => 'Magento\Backend\Block\Dashboard\Searches\Renderer\Searchquery'
             ]
         );
diff --git a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php
index a7b63d6f28acb2a7027bf01e12a021a7189cad61..364515556385b161ab976a95eba28946b976bb8d 100644
--- a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php
+++ b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php
@@ -128,17 +128,6 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
             );
         }
 
-        $fieldset->addField(
-            'synonym_for',
-            'text',
-            [
-                'name' => 'synonym_for',
-                'label' => __('Synonym For'),
-                'title' => __('Synonym For'),
-                'note' => __('Will make search for the query above return results for this search')
-            ]
-        );
-
         $fieldset->addField(
             'redirect',
             'text',
diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php
index 3c7668f719f885eeaee483734274ac317385eecc..adfafd691f641bb5af75c7a1ad571ed7f047c36b 100644
--- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php
+++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php
@@ -28,6 +28,15 @@ abstract class Synonyms extends Action
      */
     protected $registry;
 
+    /**
+     * @var \Magento\Search\Model\EngineResolver $engineResolver
+     */
+    protected $engineResolver;
+
+    /**
+     * @var \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig
+     */
+    protected $searchFeatureConfig;
 
     /**
      * Constructor
@@ -36,16 +45,22 @@ abstract class Synonyms extends Action
      * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
      * @param \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory
      * @param \Magento\Framework\Registry $registry
+     * @param \Magento\Search\Model\EngineResolver $engineResolver
+     * @param \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig
      */
     public function __construct(
         Action\Context $context,
         \Magento\Framework\View\Result\PageFactory $resultPageFactory,
         \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory,
-        \Magento\Framework\Registry $registry
+        \Magento\Framework\Registry $registry,
+        \Magento\Search\Model\EngineResolver $engineResolver,
+        \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig
     ) {
         $this->resultPageFactory = $resultPageFactory;
         $this->forwardFactory = $forwardFactory;
         $this->registry = $registry;
+        $this->engineResolver = $engineResolver;
+        $this->searchFeatureConfig = $searchFeatureConfig;
         parent::__construct($context);
     }
 
@@ -56,8 +71,11 @@ abstract class Synonyms extends Action
      */
     protected function _initAction()
     {
+        $this->checkSearchEngineSupport();
         /** @var \Magento\Backend\Model\View\Result\Page  $resultPage **/
         $resultPage = $this->resultPageFactory->create();
+
+        // Make it active on menu and set breadcrumb trail
         $resultPage->setActiveMenu('Magento_Search::search_synonyms');
         $resultPage->addBreadcrumb(__('Marketing'), __('Marketing'));
         $resultPage->addBreadcrumb(__('Search Synonyms'), __('Search Synonyms'));
@@ -71,4 +89,30 @@ abstract class Synonyms extends Action
     {
         return $this->_authorization->isAllowed('Magento_Search::synonyms');
     }
+
+    /**
+     * Checks if 'synonyms' feature is supported by configured search engine. If not supported displays a notice
+     *
+     * @return void
+     */
+    protected function checkSearchEngineSupport()
+    {
+        // Display a notice if search engine configuration does not support synonyms
+        $searchEngine = $this->engineResolver->getCurrentSearchEngine();
+        if (!$this->searchFeatureConfig
+            ->isFeatureSupported(
+                \Magento\Framework\Search\SearchEngine\ConfigInterface::SEARCH_ENGINE_FEATURE_SYNONYMS,
+                $searchEngine
+            )
+        ) {
+            $this->messageManager
+                ->addNoticeMessage(
+                    __(
+                        'Search synonyms are not supported by the %1 search engine. '
+                        . 'Any synonyms you enter won\'t be used.',
+                        $searchEngine
+                    )
+                );
+        }
+    }
 }
diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php
index 4113439bcca175f039cc757260c9b8dd0af5f19b..364249f53c3f9c31090834ab6f9857f9338fd9ed 100644
--- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php
+++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php
@@ -6,10 +6,12 @@
 
 namespace Magento\Search\Controller\Adminhtml\Synonyms;
 
+use Magento\Search\Controller\Adminhtml\Synonyms;
+
 /**
  * Delete Controller
  */
-class Delete extends \Magento\Backend\App\Action
+class Delete extends Synonyms
 {
     /**
      * Delete action
diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php
index ef3b99ad52f92f5c5fd34254449c50e09a1a249a..9dbfb3dbb0e795adc3aadac552c8e7bddf1aabbd 100644
--- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php
+++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php
@@ -6,36 +6,55 @@
 
 namespace Magento\Search\Controller\Adminhtml\Synonyms;
 
-use Magento\Framework\Controller\ResultFactory;
-use Magento\Backend\App\Action\Context;
-use Magento\Ui\Component\MassAction\Filter;
-use Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory;
+use Magento\Search\Controller\Adminhtml\Synonyms;
 
 /**
  * Mass-Delete Controller
  */
-class MassDelete extends \Magento\Backend\App\Action
+class MassDelete extends Synonyms
 {
     /**
-     * @var Filter
+     * @var \Magento\Ui\Component\MassAction\Filter
      */
     protected $filter;
 
     /**
-     * @var CollectionFactory
+     * @var \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory
      */
     protected $collectionFactory;
 
     /**
-     * @param Context $context
-     * @param Filter $filter
-     * @param CollectionFactory $collectionFactory
+     * MassDelete constructor.
+     *
+     * @param \Magento\Backend\App\Action\Context $context
+     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
+     * @param \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory
+     * @param \Magento\Framework\Registry $registry
+     * @param \Magento\Search\Model\EngineResolver $engineResolver
+     * @param \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig
+     * @param \Magento\Ui\Component\MassAction\Filter $filter
+     * @param \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory $collectionFactory
      */
-    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
-    {
+    public function __construct(
+        \Magento\Backend\App\Action\Context $context,
+        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
+        \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory,
+        \Magento\Framework\Registry $registry,
+        \Magento\Search\Model\EngineResolver $engineResolver,
+        \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig,
+        \Magento\Ui\Component\MassAction\Filter $filter,
+        \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory $collectionFactory
+    ) {
         $this->filter = $filter;
         $this->collectionFactory = $collectionFactory;
-        parent::__construct($context);
+        parent::__construct(
+            $context,
+            $resultPageFactory,
+            $forwardFactory,
+            $registry,
+            $engineResolver,
+            $searchFeatureConfig
+        );
     }
 
     /**
@@ -72,7 +91,7 @@ class MassDelete extends \Magento\Backend\App\Action
             );
         }
         /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
-        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
+        $resultRedirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT);
         return $resultRedirect->setPath('*/*/');
     }
 }
diff --git a/app/code/Magento/Search/Model/Query.php b/app/code/Magento/Search/Model/Query.php
index 2819a6e2ec5702f784b3a6d3217b5dcca08b07d6..8caf25f5e3416652b715a534e3a8f1c0327cfac8 100644
--- a/app/code/Magento/Search/Model/Query.php
+++ b/app/code/Magento/Search/Model/Query.php
@@ -29,8 +29,6 @@ use Magento\Store\Model\StoreManagerInterface;
  * @method \Magento\Search\Model\Query setPopularity(int $value)
  * @method string getRedirect()
  * @method \Magento\Search\Model\Query setRedirect(string $value)
- * @method string getSynonymFor()
- * @method \Magento\Search\Model\Query setSynonymFor(string $value)
  * @method int getDisplayInTerms()
  * @method \Magento\Search\Model\Query setDisplayInTerms(int $value)
  * @method \Magento\Search\Model\Query setQueryNameExceeded(bool $value)
@@ -168,23 +166,16 @@ class Query extends AbstractModel implements QueryInterface
      *
      * @param string $text
      * @return $this
+     * @deprecated "synonym for" feature has been removed
      */
     public function loadByQuery($text)
     {
-        $this->_getResource()->loadByQuery($this, $text);
-
-        $synonymFor = $this->getSynonymFor();
-        if (!empty($synonymFor)) {
-            $this->setQueryText($synonymFor);
-        }
-
-        $this->_afterLoad();
-        $this->setOrigData();
+        $this->loadByQueryText($text);
         return $this;
     }
 
     /**
-     * Load Query object only by query text (skip 'synonym For')
+     * Load Query object only by query text
      *
      * @param string $text
      * @return $this
diff --git a/app/code/Magento/Search/Model/QueryFactory.php b/app/code/Magento/Search/Model/QueryFactory.php
index 7cafda6466c3884034a2fa6eb32b2bfbb1a93cd0..12727ca9746e76a7c387fdc41d98a39b10bc7c50 100644
--- a/app/code/Magento/Search/Model/QueryFactory.php
+++ b/app/code/Magento/Search/Model/QueryFactory.php
@@ -69,7 +69,7 @@ class QueryFactory implements QueryFactoryInterface
             $maxQueryLength = $this->getMaxQueryLength();
             $rawQueryText = $this->getRawQueryText();
             $preparedQueryText = $this->getPreparedQueryText($rawQueryText, $maxQueryLength);
-            $query = $this->create()->loadByQuery($preparedQueryText);
+            $query = $this->create()->loadByQueryText($preparedQueryText);
             if (!$query->getId()) {
                 $query->setQueryText($preparedQueryText);
             }
diff --git a/app/code/Magento/Search/Model/ResourceModel/Query.php b/app/code/Magento/Search/Model/ResourceModel/Query.php
index 9c2e3087fe5c92154debb0bc4a56b5f99c58892c..39771ba1ad422f150865d6fe9ecfabf4705790c7 100644
--- a/app/code/Magento/Search/Model/ResourceModel/Query.php
+++ b/app/code/Magento/Search/Model/ResourceModel/Query.php
@@ -90,7 +90,7 @@ class Query extends AbstractDb
         if (is_numeric($value)) {
             return parent::load($object, $value);
         } else {
-            $this->loadByQuery($object, $value);
+            $this->loadByQueryText($object, $value);
         }
         return $this;
     }
@@ -101,43 +101,14 @@ class Query extends AbstractDb
      * @param AbstractModel $object
      * @param string $value
      * @return $this
+     * @deprecated "synonym for" feature has been removed
      */
     public function loadByQuery(AbstractModel $object, $value)
     {
-        $connection = $this->getConnection();
-        $select = $connection->select();
-
-        $querySelect = $this->getQuerySelect($object, 'query_text', $value);
-        $synonymSelect = $this->getQuerySelect($object, 'synonym_for', $value);
-
-        $select->union(["($synonymSelect)", "($querySelect)"], Select::SQL_UNION_ALL)
-            ->limit(1);
-
-        $data = $this->getConnection()->fetchRow($select);
-        if ($data) {
-            $object->setData($data);
-            $this->_afterLoad($object);
-        }
-
+        $this->loadByQueryText($object, $value);
         return $this;
     }
 
-    /**
-     * @param AbstractModel $object
-     * @param string $field
-     * @param string $value
-     * @return Select
-     */
-    private function getQuerySelect(AbstractModel $object, $field, $value)
-    {
-        $select = $this->getConnection()->select();
-        $select->from($this->getMainTable())
-            ->where($field . ' = ?', $value)
-            ->where('store_id = ?', $object->getStoreId())
-            ->limit(1);
-        return $select;
-    }
-
     /**
      * @param AbstractModel $object
      * @return $this
diff --git a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php
index 9df7dc3ffaee5974cc2764235b0269498dab7167..421cd3655f8c66302f720ad3e71fcbb2530ee0c8 100644
--- a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php
+++ b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php
@@ -102,14 +102,12 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      */
     public function setQueryFilter($query)
     {
-        $ifSynonymFor = $this->getConnection()->getIfNullSql('synonym_for', 'query_text');
         $this->getSelect()->reset(
             \Magento\Framework\DB\Select::FROM
         )->distinct(
             true
         )->from(
-            ['main_table' => $this->getTable('search_query')],
-            ['query' => $ifSynonymFor, 'num_results']
+            ['main_table' => $this->getTable('search_query')]
         )->where(
             'num_results > 0 AND display_in_terms = 1 AND query_text LIKE ?',
             $this->_resourceHelper->addLikeEscape($query, ['position' => 'start'])
@@ -130,13 +128,6 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      */
     public function setPopularQueryFilter($storeIds = null)
     {
-        $ifSynonymFor = new \Zend_Db_Expr(
-            $this->getConnection()->getCheckSql(
-                "synonym_for IS NOT NULL AND synonym_for != ''",
-                'synonym_for',
-                'query_text'
-            )
-        );
 
         $this->getSelect()->reset(
             \Magento\Framework\DB\Select::FROM
@@ -145,8 +136,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
         )->distinct(
             true
         )->from(
-            ['main_table' => $this->getTable('search_query')],
-            ['name' => $ifSynonymFor, 'num_results', 'popularity', 'query_id']
+            ['main_table' => $this->getTable('search_query')]
         );
         if ($storeIds) {
             $this->addStoreFilter($storeIds);
@@ -156,7 +146,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
             $this->getSelect()->where('num_results > 0');
         }
 
-        $this->getSelect()->order(['popularity desc', 'name']);
+        $this->getSelect()->order(['popularity desc']);
 
         return $this;
     }
diff --git a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
index 661eff949c7be48bb8cea86ce590f72264f589da..149da4111eb646edb1cac0d47a0b0ef79fdcbdc9 100644
--- a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
+++ b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
@@ -153,7 +153,7 @@ class SynonymReader extends AbstractDb
     private function isSynRowForWebsite($row)
     {
         $websiteId = $this->storeManager->getStore()->getWebsiteId();
-        return ($row['website_id'] === $websiteId);
+        return (($row['website_id'] === $websiteId) && ($row['store_id'] == 0));
     }
 
     /**
diff --git a/app/code/Magento/Search/Model/SearchEngine/Config.php b/app/code/Magento/Search/Model/SearchEngine/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7d86c731be3d052e9f484aca4797f4b0a242da5
--- /dev/null
+++ b/app/code/Magento/Search/Model/SearchEngine/Config.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Model\SearchEngine;
+
+class Config implements \Magento\Framework\Search\SearchEngine\ConfigInterface
+{
+    /**
+     * Search engine config data storage
+     *
+     * @var Config\Data
+     */
+    protected $dataStorage;
+
+    /**
+     * Constructor
+     *
+     * @param \Magento\Framework\Config\DataInterface $dataStorage
+     */
+    public function __construct(\Magento\Framework\Config\DataInterface $dataStorage)
+    {
+        $this->dataStorage = $dataStorage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDeclaredFeatures($searchEngine)
+    {
+        return $this->dataStorage->get($searchEngine, []);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isFeatureSupported($featureName, $searchEngine)
+    {
+        $features = $this->getDeclaredFeatures($searchEngine);
+        return in_array(strtolower($featureName), $features);
+    }
+}
diff --git a/app/code/Magento/Search/Model/SearchEngine/Config/Data.php b/app/code/Magento/Search/Model/SearchEngine/Config/Data.php
new file mode 100644
index 0000000000000000000000000000000000000000..0e7afecdfbef7a7be8088e0eda43a20c2f4279f1
--- /dev/null
+++ b/app/code/Magento/Search/Model/SearchEngine/Config/Data.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Model\SearchEngine\Config;
+
+class Data extends \Magento\Framework\Config\Data
+{
+    /**
+     * Constructor
+     *
+     * @param \Magento\Framework\Search\SearchEngine\Config\Reader $reader
+     * @param \Magento\Framework\Config\CacheInterface $cache
+     * @param string $cacheId
+     */
+    public function __construct(
+        \Magento\Framework\Search\SearchEngine\Config\Reader $reader,
+        \Magento\Framework\Config\CacheInterface $cache,
+        $cacheId = 'search_engine_config_cache'
+    ) {
+        parent::__construct($reader, $cache, $cacheId);
+    }
+}
diff --git a/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php b/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e0f463a663e00180c93322ca397ad7f69fefdf0
--- /dev/null
+++ b/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Model\SearchEngine;
+
+use Magento\Backend\Model\Menu;
+use Magento\Backend\Model\Menu\Builder;
+use Magento\Framework\Search\SearchEngine\ConfigInterface;
+use Magento\Search\Model\EngineResolver;
+
+/**
+ * A plugin for Magento\Backend\Model\Menu\Builder class. Implements "after" for "getResult()".
+ *
+ * The purpose of this plugin is to go through the menu tree and remove "Search Terms" menu item if the
+ * selected search engine does not support "synonyms" feature.
+ */
+class MenuBuilder
+{
+    /**
+     * A constant to refer to "Search Synonyms" menu item id from etc/adminhtml/menu.xml
+     */
+    const SEARCH_SYNONYMS_MENU_ITEM_ID = 'Magento_Search::search_synonyms';
+
+    /**
+     * @var ConfigInterface $searchFeatureConfig
+     */
+    protected $searchFeatureConfig;
+
+    /**
+     * @var EngineResolver $engineResolver
+     */
+    protected $engineResolver;
+
+    /**
+     * MenuBuilder constructor.
+     *
+     * @param ConfigInterface $searchFeatureConfig
+     * @param EngineResolver $engineResolver
+     */
+    public function __construct(
+        ConfigInterface $searchFeatureConfig,
+        EngineResolver $engineResolver
+    ) {
+        $this->searchFeatureConfig = $searchFeatureConfig;
+        $this->engineResolver = $engineResolver;
+    }
+
+    /**
+     * Removes 'Search Synonyms' from the menu if 'synonyms' is not supported
+     *
+     * @param Builder $subject
+     * @param Menu $menu
+     * @return Menu
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterGetResult(Builder $subject, Menu $menu)
+    {
+        $searchEngine = $this->engineResolver->getCurrentSearchEngine();
+        if (!$this->searchFeatureConfig
+            ->isFeatureSupported(ConfigInterface::SEARCH_ENGINE_FEATURE_SYNONYMS, $searchEngine)
+        ) {
+
+            // "Search Synonyms" feature is not supported by the current configured search engine.
+            // Menu will be updated to remove it from the list
+            $menu->remove(self::SEARCH_SYNONYMS_MENU_ITEM_ID);
+        }
+        return $menu;
+    }
+}
diff --git a/app/code/Magento/Search/Model/SynonymAnalyzer.php b/app/code/Magento/Search/Model/SynonymAnalyzer.php
index 65b4f313535af90e1a5b884d32988821ad5aa831..edf24c642dfd4ac27b3e2c12efa907d6f3ef007c 100644
--- a/app/code/Magento/Search/Model/SynonymAnalyzer.php
+++ b/app/code/Magento/Search/Model/SynonymAnalyzer.php
@@ -6,7 +6,6 @@
 namespace Magento\Search\Model;
 
 use Magento\Search\Api\SynonymAnalyzerInterface;
-use Magento\Search\Model\SynonymReader;
 
 class SynonymAnalyzer implements SynonymAnalyzerInterface
 {
@@ -49,17 +48,6 @@ class SynonymAnalyzer implements SynonymAnalyzerInterface
             return $synGroups;
         }
 
-        // strip off all the white spaces, comma, semicolons, and other such
-        // "non-word" characters. Then implode it into a single string using white space as delimiter
-        //$words = preg_split('/\W+/', strtolower($phrase), -1, PREG_SPLIT_NO_EMPTY);
-        $words = preg_split(
-            '/[~`!@#$%^&*()_+={}\[\]:"\',\s\.<>?\/\;\\\]+/',
-            strtolower($phrase),
-            -1,
-            PREG_SPLIT_NO_EMPTY
-        );
-        $phrase = implode(' ', $words);
-
         $rows = $this->synReaderModel->loadByPhrase($phrase)->getData();
         $synonyms = [];
         foreach ($rows as $row) {
@@ -68,6 +56,7 @@ class SynonymAnalyzer implements SynonymAnalyzerInterface
 
         // Go through every returned record looking for presence of the actual phrase. If there were no matching
         // records found in DB then create a new entry for it in the returned array
+        $words = explode(' ', $phrase);
         foreach ($words as $w) {
             $position = $this->findInArray($w, $synonyms);
             if ($position !== false) {
diff --git a/app/code/Magento/Search/Setup/UpgradeSchema.php b/app/code/Magento/Search/Setup/UpgradeSchema.php
index 4afd818ac7f7745206628027beb68087341a9740..1477456a596a5f7568adc00d9d9001cefb00a0bf 100644
--- a/app/code/Magento/Search/Setup/UpgradeSchema.php
+++ b/app/code/Magento/Search/Setup/UpgradeSchema.php
@@ -165,5 +165,13 @@ class UpgradeSchema implements UpgradeSchemaInterface
 
             $connection->createTable($table);
         }
+
+        if (version_compare($context->getVersion(), '2.0.4') < 0) {
+            $connection->dropIndex(
+                $setup->getTable('search_query'),
+                $installer->getIdxName('search_query', 'synonym_for')
+            );
+            $connection->dropColumn($setup->getTable('search_query'), 'synonym_for');
+        }
     }
 }
diff --git a/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php b/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7922f4996d088c607901ee74bbee723eacb3880
--- /dev/null
+++ b/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Test\Unit\Adapter\Mysql\Query\Preprocessor;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class SynonymsTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Search\Api\SynonymAnalyzerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $synonymAnalyzer;
+
+    /**
+     * @var \Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms
+     */
+    private $synonymPreprocessor;
+
+    protected function setUp()
+    {
+        $objectManager = new ObjectManager($this);
+
+        $this->synonymAnalyzer = $this->getMockBuilder('Magento\Search\Model\SynonymAnalyzer')
+            ->setMethods(['getSynonymsForPhrase'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->synonymPreprocessor = $objectManager->getObject(
+            'Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms',
+            [
+                'synonymsAnalyzer' => $this->synonymAnalyzer
+            ]
+        );
+    }
+
+    /**
+     * Data provider for the test
+     *
+     * @return array
+     */
+    public static function loadProcessDataProvider()
+    {
+        return [
+            'oneWord' => [
+                'query' => 'big',
+                'result' => [['big', 'huge']],
+                'newQuery' => 'big huge'
+            ],
+            'twoWords' => [
+                'query' => 'big universe',
+                'result' => [['big', 'huge'], ['universe', 'cosmos']],
+                'newQuery' => 'big huge universe cosmos'
+            ],
+            'noSynonyms' => [
+                'query' => 'no synonyms',
+                'result' => [['no'], ['synonyms']],
+                'newQuery' => 'no synonyms'
+            ]
+        ];
+    }
+
+    /**
+     * @param string $phrase
+     * @param array $expectedResult
+     * @dataProvider loadProcessDataProvider
+     */
+    public function testProcess($query, $result, $newQuery)
+    {
+        $this->synonymAnalyzer->expects($this->once())
+            ->method('getSynonymsForPhrase')
+            ->with($this->equalTo($query))
+            ->will($this->returnValue($result));
+
+        $result = $this->synonymPreprocessor->process($query);
+        $this->assertEquals($result, $newQuery);
+    }
+}
diff --git a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php
index 2608fbae4b275d2b5764e3f0f4a20d8f3a2114dc..382e96ecde84943372e60606f2d2b2e688eb39d5 100644
--- a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php
+++ b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php
@@ -68,7 +68,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue($this->scopeConfigMock));
         $this->queryMock = $this->getMockBuilder('\Magento\Search\Model\Query')
             ->disableOriginalConstructor()
-            ->setMethods(['setIsQueryTextExceeded', 'getId', 'setQueryText', 'loadByQuery'])
+            ->setMethods(['setIsQueryTextExceeded', 'getId', 'setQueryText', 'loadByQueryText'])
             ->getMock();
         $this->objectManagerHelper = new ObjectManagerHelper($this);
         $this->queryFactory = $this->objectManagerHelper->getObject(
@@ -102,7 +102,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase
             ->with($this->equalTo('Magento\Search\Model\Query'))
             ->will($this->returnValue($this->queryMock));
         $this->queryMock->expects($this->once())
-            ->method('loadByQuery')
+            ->method('loadByQueryText')
             ->with($this->equalTo($preparedQueryText))
             ->will($this->returnSelf());
         $this->queryMock->expects($this->once())
@@ -141,7 +141,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase
             ->with($this->equalTo('Magento\Search\Model\Query'))
             ->will($this->returnValue($this->queryMock));
         $this->queryMock->expects($this->once())
-            ->method('loadByQuery')
+            ->method('loadByQueryText')
             ->with($this->equalTo($preparedQueryText))
             ->will($this->returnSelf());
         $this->queryMock->expects($this->once())
@@ -179,7 +179,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase
             ->with($this->equalTo('Magento\Search\Model\Query'))
             ->will($this->returnValue($this->queryMock));
         $this->queryMock->expects($this->once())
-            ->method('loadByQuery')
+            ->method('loadByQueryText')
             ->with($this->equalTo($preparedQueryText))
             ->will($this->returnSelf());
         $this->queryMock->expects($this->once())
@@ -218,7 +218,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase
             ->with($this->equalTo('Magento\Search\Model\Query'))
             ->will($this->returnValue($this->queryMock));
         $this->queryMock->expects($this->once())
-            ->method('loadByQuery')
+            ->method('loadByQueryText')
             ->with($this->equalTo($preparedQueryText))
             ->will($this->returnSelf());
         $this->queryMock->expects($this->once())
diff --git a/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cd1f5ba84e628108b8cb5c783bd1831677197c6
--- /dev/null
+++ b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Test\Unit\Model\SearchEngine;
+
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var \Magento\Search\Model\SearchEngine\Config\Data|\PHPUnit_Framework_MockObject_MockObject */
+    protected $dataStorageMock;
+
+    /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */
+    protected $objectManager;
+
+    protected function setUp()
+    {
+        $this->dataStorage = $this->getMock('Magento\Search\Model\SearchEngine\Config\Data', [], [], '', false);
+        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+    }
+
+    public function testGetDeclaredFeatures()
+    {
+        $config = $this->objectManager->getObject(
+            '\Magento\Search\Model\SearchEngine\Config',
+            ['dataStorage' => $this->dataStorage]
+        );
+        $this->dataStorage->expects($this->once())->method('get')->with('mysql')->willReturn(['synonyms']);
+        $this->assertEquals(['synonyms'], $config->getDeclaredFeatures('mysql'));
+    }
+
+    public function testIsFeatureSupported()
+    {
+        $config = $this->objectManager->getObject(
+            '\Magento\Search\Model\SearchEngine\Config',
+            ['dataStorage' => $this->dataStorage]
+        );
+        $this->dataStorage->expects($this->once())->method('get')->with('mysql')->willReturn(['synonyms']);
+        $this->assertEquals(true, $config->isFeatureSupported('synonyms', 'mysql'));
+    }
+}
diff --git a/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..37347ee9136fe92cf26acd3abde628a28b767eb1
--- /dev/null
+++ b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Test\Unit\Model\SearchEngine;
+
+use Magento\Backend\Model\Menu;
+use Magento\Backend\Model\Menu\Builder;
+use Magento\Framework\Search\SearchEngine\ConfigInterface;
+use Magento\Search\Model\EngineResolver;
+
+/**
+ * Class MenuBuilderTest. A unit test class to test functionality of
+ * Magento\Search\Model\SearchEngine\MenuBuilder class
+ */
+class MenuBuilderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $searchFeatureConfig;
+
+    /**
+     * @var EngineResolver|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $engineResolver;
+
+    protected function setUp()
+    {
+        $this->searchFeatureConfig = $this->getMock('\Magento\Search\Model\SearchEngine\Config', [], [], '', false);
+        $this->engineResolver = $this->getMock('\Magento\Search\Model\EngineResolver', [], [], '', false);
+    }
+
+    public function testAfterGetResult()
+    {
+        $this->engineResolver->expects($this->once())->method('getCurrentSearchEngine')->willReturn('mysql');
+        $this->searchFeatureConfig
+            ->expects($this->once())
+            ->method('isFeatureSupported')
+            ->with('synonyms', 'mysql')
+            ->willReturn(false);
+        /** @var \Magento\Backend\Model\Menu $menu */
+        $menu = $this->getMock('\Magento\Backend\Model\Menu', [], [], '', false);
+        $menu->expects($this->once())->method('remove')->willReturn(true);
+
+        /** @var \Magento\Backend\Model\Menu\Builder $menuBuilder */
+        $menuBuilder = $this->getMock('\Magento\Backend\Model\Menu\Builder', [], [], '', false);
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        /** @var \Magento\Search\Model\SearchEngine\MenuBuilder $searchMenuBuilder */
+        $searchMenuBuilder = $objectManager->getObject(
+            'Magento\Search\Model\SearchEngine\MenuBuilder',
+            [
+                'searchFeatureConfig' => $this->searchFeatureConfig,
+                'engineResolver' => $this->engineResolver
+            ]
+        );
+        $this->assertInstanceOf(
+            '\Magento\Backend\Model\Menu',
+            $searchMenuBuilder->afterGetResult($menuBuilder, $menu)
+        );
+    }
+}
diff --git a/app/code/Magento/Search/etc/di.xml b/app/code/Magento/Search/etc/di.xml
index ab7974cbbbe809cc2b81e79d95905dbea3dc3568..9a8d20e8ef126558c47c9ac93aa44600a0451ff7 100755
--- a/app/code/Magento/Search/etc/di.xml
+++ b/app/code/Magento/Search/etc/di.xml
@@ -67,4 +67,21 @@
     </type>
     <preference for="Magento\Search\Api\Data\SynonymGroupInterface" type="Magento\Search\Model\SynonymGroup" />
     <preference for="Magento\Search\Api\SynonymGroupRepositoryInterface" type="Magento\Search\Model\SynonymGroupRepository" />
+    <preference for="Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface" type="Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms" />
+    <type name="Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match">
+        <arguments>
+            <argument name="preprocessors" xsi:type="array">
+                <item name="synonymsPreprocessor" xsi:type="object">Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms</item>
+            </argument>
+        </arguments>
+    </type>
+    <preference for="Magento\Framework\Search\SearchEngine\ConfigInterface" type="Magento\Search\Model\SearchEngine\Config" />
+    <type name="Magento\Search\Model\SearchEngine\Config">
+        <arguments>
+            <argument name="dataStorage" xsi:type="object">Magento\Search\Model\SearchEngine\Config\Data</argument>
+        </arguments>
+    </type>
+    <type name="Magento\Backend\Model\Menu\Builder">
+        <plugin name="SearchTermMenuBuilder" type="Magento\Search\Model\SearchEngine\MenuBuilder" />
+    </type>
 </config>
diff --git a/app/code/Magento/Search/etc/module.xml b/app/code/Magento/Search/etc/module.xml
index 5d873785d9f7c69b1c5ef709e4949ae45093b6f0..2579783347edd72b16b30c53e6239abd41a5bdcc 100644
--- a/app/code/Magento/Search/etc/module.xml
+++ b/app/code/Magento/Search/etc/module.xml
@@ -7,7 +7,7 @@
 -->
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
-    <module name="Magento_Search" setup_version="2.0.3">
+    <module name="Magento_Search" setup_version="2.0.4">
         <sequence>
             <module name="Magento_Backend" />
         </sequence>
diff --git a/app/code/Magento/Search/etc/search_engine.xml b/app/code/Magento/Search/etc/search_engine.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c46bf5caa954e1b64191fb0b13e142e54e5e005d
--- /dev/null
+++ b/app/code/Magento/Search/etc/search_engine.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
+    <engine name="mysql">
+        <feature name="synonyms" support="true" />
+    </engine>
+</engines>
diff --git a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml
index ed5954ed02b21b48ff0cfe57c8d25f934d3fcc07..6d827e8575e4484f8c760881453a570ebe9557b1 100644
--- a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml
+++ b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml
@@ -69,12 +69,6 @@
                             <argument name="type" xsi:type="string">number</argument>
                         </arguments>
                     </block>
-                    <block class="Magento\Backend\Block\Widget\Grid\Column" as="synonym_for">
-                        <arguments>
-                            <argument name="header" xsi:type="string" translate="true">Synonym</argument>
-                            <argument name="index" xsi:type="string">synonym_for</argument>
-                        </arguments>
-                    </block>
                     <block class="Magento\Backend\Block\Widget\Grid\Column" as="redirect">
                         <arguments>
                             <argument name="header" xsi:type="string" translate="true">Redirect URL</argument>
diff --git a/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php b/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php
index 330bec5c52b755ead7bc57e1eeab3eb6f9df89ea..7139300904e444e63a85d42347e708d95403af95 100644
--- a/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php
+++ b/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php
@@ -186,6 +186,9 @@ class LabelGenerator
         $pdfImage = \Zend_Pdf_Image::imageWithPath($tmpFileName);
         $page->drawImage($pdfImage, 0, 0, $xSize, $ySize);
         $directory->delete($directory->getRelativePath($tmpFileName));
+        if (is_resource($image)) {
+            imagedestroy($image);
+        }
         return $page;
     }
 }
diff --git a/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php b/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php
index 9c833fa0db6a0805fc3ee13b984ae44b23ff2a28..bac47e50d80ed52f5bf7de4f7a19e9ba534a8c92 100644
--- a/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php
+++ b/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php
@@ -38,6 +38,11 @@ class StoreTest extends \PHPUnit_Framework_TestCase
      */
     protected $escaperMock;
 
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
     /**
      * @var string
      */
@@ -68,6 +73,10 @@ class StoreTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($this->processorMock);
         $this->processorMock->expects($this->atLeastOnce())->method('register');
+        $this->storeManagerMock = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
         $this->model = $objectManager->getObject(
             'Magento\Store\Ui\Component\Listing\Column\Store',
             [
@@ -79,10 +88,48 @@ class StoreTest extends \PHPUnit_Framework_TestCase
                 'data' => ['name' => $this->name]
             ]
         );
+
+        $this->injectMockedDependency($this->storeManagerMock, 'storeManager');
+    }
+
+    /**
+     * Inject mocked object dependency
+     *
+     * @param \PHPUnit_Framework_MockObject_MockObject $mockObject
+     * @param string $propertyName
+     * @return void
+     *
+     * @deprecated
+     */
+    private function injectMockedDependency($mockObject, $propertyName)
+    {
+        $reflection = new \ReflectionClass(get_class($this->model));
+        $reflectionProperty = $reflection->getProperty($propertyName);
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($this->model, $mockObject);
+    }
+
+    public function testPrepare()
+    {
+        $this->storeManagerMock->expects($this->atLeastOnce())->method('isSingleStoreMode')->willReturn(false);
+        $this->model->prepare();
+        $config = $this->model->getDataByKey('config');
+        $this->assertEmpty($config);
+    }
+
+    public function testPrepareWithSingleStore()
+    {
+        $this->storeManagerMock->expects($this->atLeastOnce())->method('isSingleStoreMode')->willReturn(true);
+        $this->model->prepare();
+        $config = $this->model->getDataByKey('config');
+        $this->assertNotEmpty($config);
+        $this->assertArrayHasKey('componentDisabled', $config);
+        $this->assertTrue($config['componentDisabled']);
     }
 
     /**
      * @dataProvider prepareDataSourceDataProvider
+     * @deprecated
      */
     public function testPrepareDataSource($dataSource, $expectedResult)
     {
@@ -112,6 +159,9 @@ class StoreTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($this->model->prepareDataSource($dataSource), $expectedResult);
     }
 
+    /**
+     * @deprecated
+     */
     public function prepareDataSourceDataProvider()
     {
         $content = "website<br/>&nbsp;&nbsp;&nbsp;group<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;store<br/>";
diff --git a/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php b/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php
new file mode 100644
index 0000000000000000000000000000000000000000..c43ddd6c7f0d0a559c87805d301f22214ff61c5a
--- /dev/null
+++ b/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Store\Ui\Component\Form\Fieldset;
+
+use Magento\Framework\View\Element\UiComponentInterface;
+use Magento\Framework\View\Element\UiComponent\ContextInterface;
+use Magento\Ui\Component\Form\Fieldset;
+use Magento\Store\Model\StoreManagerInterface as StoreManager;
+
+/**
+ * Class Websites Fieldset
+ */
+class Websites extends Fieldset
+{
+    /**
+     * Store manager
+     *
+     * @var StoreManager
+     */
+    protected $storeManager;
+
+    /**
+     * Constructor
+     *
+     * @param ContextInterface $context
+     * @param StoreManager $storeManager
+     * @param UiComponentInterface[] $components
+     * @param array $data
+     */
+    public function __construct(
+        ContextInterface $context,
+        StoreManager $storeManager,
+        array $components = [],
+        array $data = []
+    ) {
+        parent::__construct($context, $components, $data);
+        $this->storeManager = $storeManager;
+
+    }
+
+    /**
+     * Prepare component configuration
+     *
+     * @return void
+     */
+    public function prepare()
+    {
+        parent::prepare();
+        if ($this->storeManager->isSingleStoreMode()) {
+            $this->_data['config']['componentDisabled'] = true;
+        }
+    }
+}
diff --git a/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php b/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php
index 6a520c36392ca9c27dc86336e9de96200fc29ed1..78520cce1270cb5b6a8fd068da348c1ffb4f32d8 100644
--- a/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php
+++ b/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php
@@ -10,6 +10,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface;
 use Magento\Framework\View\Element\UiComponentFactory;
 use Magento\Store\Model\System\Store as SystemStore;
 use Magento\Ui\Component\Listing\Columns\Column;
+use Magento\Store\Model\StoreManagerInterface as StoreManager;
 
 /**
  * Class Store
@@ -30,6 +31,13 @@ class Store extends Column
      */
     protected $systemStore;
 
+    /**
+     * Store manager
+     *
+     * @var StoreManager
+     */
+    protected $storeManager;
+
     /**
      * @var string
      */
@@ -111,4 +119,33 @@ class Store extends Column
 
         return $content;
     }
+
+    /**
+     * Prepare component configuration
+     *
+     * @return void
+     */
+    public function prepare()
+    {
+        parent::prepare();
+        if ($this->getStoreManager()->isSingleStoreMode()) {
+            $this->_data['config']['componentDisabled'] = true;
+        }
+    }
+
+    /**
+     * Get StoreManager dependency
+     *
+     * @return StoreManager
+     *
+     * @deprecated
+     */
+    private function getStoreManager()
+    {
+        if ($this->storeManager === null) {
+            $this->storeManager = \Magento\Framework\App\ObjectManager::getInstance()
+                ->get('Magento\Store\Model\StoreManagerInterface');
+        }
+        return $this->storeManager;
+    }
 }
diff --git a/app/code/Magento/Translation/Model/FileManager.php b/app/code/Magento/Translation/Model/FileManager.php
index 1e6a18acc8cd1a95ab16732e7a9d97bb27d99e4e..bfef51599a2261017cf52f35c888632e376866aa 100644
--- a/app/code/Magento/Translation/Model/FileManager.php
+++ b/app/code/Magento/Translation/Model/FileManager.php
@@ -96,6 +96,15 @@ class FileManager
      */
     public function updateTranslationFileContent($content)
     {
+        $translationDir = $this->directoryList->getPath(DirectoryList::STATIC_VIEW) .
+            \DIRECTORY_SEPARATOR .
+            $this->assetRepo->getStaticViewFileContext()->getPath();
+        if (!$this->driverFile->isExists($this->getTranslationFileFullPath())) {
+            $this->driverFile->createDirectory(
+                $translationDir,
+                \Magento\Framework\Filesystem\Driver\File::WRITEABLE_DIRECTORY_MODE
+            );
+        }
         $this->driverFile->filePutContents($this->getTranslationFileFullPath(), $content);
     }
 }
diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php
index 16f73d8237d04ac1c0c8479259f64520f337dfd3..03f2cb7afddfe35ca5e418969df36cb9b735c1a0 100644
--- a/app/code/Magento/Translation/Model/Inline/Parser.php
+++ b/app/code/Magento/Translation/Model/Inline/Parser.php
@@ -141,21 +141,6 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface
         return $this->cacheManager;
     }
 
-    /**
-     * For unit testing purpose only
-     *
-     * @param \Magento\Translation\Model\Inline\CacheManager $cacheManager
-     * @return void
-     * @deprecated
-     */
-    public function setCacheManager(\Magento\Translation\Model\Inline\CacheManager $cacheManager)
-    {
-        if ($this->cacheManager != null) {
-            throw new \LogicException(__('cacheManager is already set'));
-        }
-        $this->cacheManager = $cacheManager;
-    }
-
     /**
      * Initialize base inline translation model
      *
@@ -186,12 +171,12 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface
      * Parse and save edited translation
      *
      * @param array $translateParams
-     * @return $this
+     * @return array
      */
     public function processAjaxPost(array $translateParams)
     {
         if (!$this->_translateInline->isAllowed()) {
-            return $this;
+            return ['inline' => 'not allowed'];
         }
         $this->_appCache->invalidate(\Magento\Framework\App\Cache\Type\Translate::TYPE_IDENTIFIER);
 
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
index b988bdde83be4e42dc4284da6e7188e00a775536..d495dbce6603f782178e51f17275c40cfd1fba2e 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
@@ -45,9 +45,20 @@ define([
          * @returns {Search} Chainable.
          */
         initialize: function () {
+            var urlParams = window.location.href.slice(window.location.href.search('[\&\?](search=)')).split('&'),
+                searchTerm = [];
+
             this._super()
                 .initChips();
 
+            if (urlParams[0]) {
+                searchTerm = urlParams[0].split('=');
+
+                if (searchTerm[1]) {
+                    this.apply(decodeURIComponent(searchTerm[1]));
+                }
+            }
+
             return this;
         },
 
diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
index 11e3f414ca553394a983801ac69afe740d542563..ec04453079d143e696535f418ddcff3dcdf1e06b 100644
--- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
+++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
@@ -18,6 +18,11 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      */
     private $_cache;
 
+    /**
+     * @var array
+     */
+    private $layoutUpdateCache;
+
     /**
      * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
      * @param \Magento\Framework\Cache\FrontendInterface $cache
@@ -55,14 +60,17 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         \Magento\Framework\View\Design\ThemeInterface $theme,
         \Magento\Framework\App\ScopeInterface $store
     ) {
-        $bind = ['layout_update_handle' => $handle, 'theme_id' => $theme->getId(), 'store_id' => $store->getId()];
-        $result = '';
-        $connection = $this->getConnection();
-        if ($connection) {
-            $select = $this->_getFetchUpdatesByHandleSelect();
-            $result = join('', $connection->fetchCol($select, $bind));
+        $bind = ['theme_id' => $theme->getId(), 'store_id' => $store->getId()];
+        $cacheKey = implode('-', $bind);
+        if (!isset($this->layoutUpdateCache[$cacheKey])) {
+            foreach ($this->getConnection()->fetchAll($this->_getFetchUpdatesByHandleSelect(), $bind) as $layout) {
+                if (!isset($this->layoutUpdateCache[$cacheKey][$layout['handle']])) {
+                    $this->layoutUpdateCache[$cacheKey][$layout['handle']] = '';
+                }
+                $this->layoutUpdateCache[$cacheKey][$layout['handle']] .= $layout['xml'];
+            }
         }
-        return $result;
+        return isset($this->layoutUpdateCache[$cacheKey][$handle]) ? $this->layoutUpdateCache[$cacheKey][$handle] : '';
     }
 
     /**
@@ -79,7 +87,7 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
 
         $select = $this->getConnection()->select()->from(
             ['layout_update' => $this->getMainTable()],
-            ['xml']
+            ['xml', 'handle']
         )->join(
             ['link' => $this->getTable('layout_link')],
             'link.layout_update_id=layout_update.layout_update_id',
@@ -88,8 +96,6 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
             'link.store_id IN (0, :store_id)'
         )->where(
             'link.theme_id = :theme_id'
-        )->where(
-            'layout_update.handle = :layout_update_handle'
         )->order(
             'layout_update.sort_order ' . \Magento\Framework\DB\Select::SQL_ASC
         );
diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist
index b9ea95cca04a91402ca4f69c9be2cbc403867239..5138d64ee83540d375a91bc64ee35f72a3b7cf53 100644
--- a/dev/tests/api-functional/phpunit.xml.dist
+++ b/dev/tests/api-functional/phpunit.xml.dist
@@ -35,11 +35,11 @@
         <!-- WebSerivice Type. Possible values: soap, rest -->
         <const name="TESTS_WEB_API_ADAPTER" value="rest"/>
         <!-- Webserver URL -->
-        <const name="TESTS_BASE_URL" value="http://local.puphpet/develop/magento2ce"/>
+        <const name="TESTS_BASE_URL" value="http://magento.url"/>
         <!-- Webserver API user -->
         <const name="TESTS_WEBSERVICE_USER" value="admin"/>
         <!-- Webserver API key -->
-        <const name="TESTS_WEBSERVICE_APIKEY" value="vgWeAsD!15"/>
+        <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/>
         <!-- Define if debugger should be started using XDEBUG_SESSION cookie -->
         <const name="TESTS_XDEBUG_ENABLED" value="false"/>
         <!-- Define XDEBUG_SESSION cookie value-->
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
index fc622bc2183a9ef34142e3d7a369626506f909d4..837fbb7a51e902cf0865524b04cb6217e333ff09 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
@@ -157,7 +157,8 @@ class CategoryRepositoryTest extends WebapiAbstract
     {
         $categoryId = 333;
         $categoryData = [
-            'name' => "Update Category Test",
+            'name' => 'Update Category Test',
+            'is_active' => false,
             'custom_attributes' => [
                 [
                     'attribute_code' => 'description',
@@ -170,6 +171,7 @@ class CategoryRepositoryTest extends WebapiAbstract
         /** @var \Magento\Catalog\Model\Category $model */
         $model = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Category');
         $category = $model->load($categoryId);
+        $this->assertFalse((bool)$category->getIsActive(), 'Category "is_active" must equal to false');
         $this->assertEquals("Update Category Test", $category->getName());
         $this->assertEquals("Update Category Description Test", $category->getDescription());
         // delete category to clean up auto-generated url rewrites
@@ -179,12 +181,11 @@ class CategoryRepositoryTest extends WebapiAbstract
     protected function getSimpleCategoryData($categoryData = [])
     {
         return [
-            'path' => '2',
             'parent_id' => '2',
             'name' => isset($categoryData['name'])
                 ? $categoryData['name'] : uniqid('Category-', true),
             'is_active' => '1',
-            'include_in_menu' => "1",
+            'include_in_menu' => '1',
             'available_sort_by' => ['position', 'name'],
             'custom_attributes' => [
                 ['attribute_code' => 'url_key', 'value' => ''],
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php
index dcb84bd552027feece69841952457ad583076c67..b6d160e467cef198dbd29e23aca54327d4948252 100644
--- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php
@@ -43,13 +43,14 @@ class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract
     }
 
     /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_simple_77.php
      * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      * @magentoApiDataFixture Magento/ConfigurableProduct/_files/delete_association.php
      */
     public function testAddChild()
     {
         $productSku = 'configurable';
-        $childSku = 'simple_10';
+        $childSku = 'simple_77';
         $serviceInfo = [
             'rest' => [
                 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku . '/child',
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
index def73eeb01bf131cd2fbd35f1179d07389d41f4a..e1f7ef7e4dbf0d8a378bf026c9bbe7713eb508c1 100644
--- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
@@ -187,11 +187,12 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac
             ]
         ];
 
-        $option = [
-            'label' => 'Update Test Configurable'
+        $requestBody = [
+            'option' => [
+                'label' => 'Update Test Configurable',
+            ]
         ];
 
-        $requestBody = ['option' => $option];
         if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
             $requestBody['sku'] = $productSku;
             $requestBody['option']['id'] = $optionId;
@@ -200,7 +201,7 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac
         $result = $this->_webApiCall($serviceInfo, $requestBody);
         $this->assertGreaterThan(0, $result);
         $configurableAttribute = $this->getConfigurableAttribute($productSku);
-        $this->assertEquals($option['label'], $configurableAttribute[0]['label']);
+        $this->assertEquals($requestBody['option']['label'], $configurableAttribute[0]['label']);
     }
 
     /**
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php
index 21f313bcf0c3515ace3d47586740cde1b1f4dd68..16a97a9b3c9152a2dac6a784ded1358e089ef7a3 100644
--- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php
@@ -191,6 +191,7 @@ class ProductRepositoryTest extends WebapiAbstract
         $option = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"][0];
 
         $optionId = $option['id'];
+        $productId = $option['product_id'];
         $updatedOption = [
             'id' => $optionId,
             'attribute_id' => $option['attribute_id'],
@@ -201,7 +202,7 @@ class ProductRepositoryTest extends WebapiAbstract
                     'value_index' => $option['values'][0]['value_index'],
                 ],
             ],
-            'product_id' => $response['id'],
+            'product_id' => $productId,
         ];
         $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'][0] =
             $updatedOption;
@@ -282,7 +283,7 @@ class ProductRepositoryTest extends WebapiAbstract
             $productId1, $nonExistingId
         ];
 
-        $expectedMessage = 'Product with id "%1" does not exist.';
+        $expectedMessage = 'Unable to save product';
         try {
             $this->saveProduct($response);
             $this->fail("Expected exception");
@@ -295,7 +296,6 @@ class ProductRepositoryTest extends WebapiAbstract
         } catch (\Exception $e) {
             $errorObj = $this->processRestExceptionResult($e);
             $this->assertEquals($expectedMessage, $errorObj['message']);
-            $this->assertEquals(['0' => '999'], $errorObj['parameters']);
         }
     }
 
@@ -326,7 +326,7 @@ class ProductRepositoryTest extends WebapiAbstract
             $productId1, $productId2
         ];
 
-        $expectedMessage = 'Products "%1" and %2 have the same set of attribute values.';
+        $expectedMessage = 'Products "%1" and "%2" have the same set of attribute values.';
         try {
             $this->saveProduct($response);
             $this->fail("Expected exception");
@@ -348,8 +348,8 @@ class ProductRepositoryTest extends WebapiAbstract
      */
     public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes()
     {
-        $productId1 = 10;
-        $productId2 = 20;
+        $productId1 = 99;
+        $productId2 = 88;
 
         $response = $this->createConfigurableProduct();
 
@@ -359,7 +359,7 @@ class ProductRepositoryTest extends WebapiAbstract
             $productId1, $productId2
         ];
 
-        $expectedMessage = 'The configurable product does not have any variation attribute.';
+        $expectedMessage = 'Unable to save product';
         try {
             $this->saveProduct($response);
             $this->fail("Expected exception");
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
index 29734912ef8e94cea3a29ac2ccd0a29649f1207c..7494fadfadae3ff5077eae780e1fc713f8c95e52 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
@@ -26,7 +26,7 @@
             <data name="category/data/display_mode" xsi:type="string">Static block and products</data>
             <data name="category/data/is_anchor" xsi:type="string">No</data>
             <data name="category/data/available_product_listing_config" xsi:type="string">No</data>
-            <data name="category/data/available_sort_by/sort_0" xsi:type="string">Name</data>
+            <data name="category/data/available_sort_by/sort_0" xsi:type="string">Product Name</data>
             <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data>
             <data name="category/data/default_product_listing_config" xsi:type="string">No</data>
             <data name="category/data/default_sort_by" xsi:type="string">Price</data>
@@ -67,7 +67,7 @@
             <data name="category/data/display_mode" xsi:type="string">Static block and products</data>
             <data name="category/data/is_anchor" xsi:type="string">No</data>
             <data name="category/data/available_product_listing_config" xsi:type="string">No</data>
-            <data name="category/data/available_sort_by/sort_0" xsi:type="string">Name</data>
+            <data name="category/data/available_sort_by/sort_0" xsi:type="string">Product Name</data>
             <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data>
             <data name="category/data/default_product_listing_config" xsi:type="string">No</data>
             <data name="category/data/default_sort_by" xsi:type="string">Price</data>
@@ -102,7 +102,7 @@
             <data name="category/data/is_anchor" xsi:type="string">Yes</data>
             <data name="category/data/available_product_listing_config" xsi:type="string">No</data>
             <data name="category/data/available_sort_by/sort_0" xsi:type="string">Position</data>
-            <data name="category/data/available_sort_by/sort_1" xsi:type="string">Name</data>
+            <data name="category/data/available_sort_by/sort_1" xsi:type="string">Product Name</data>
             <data name="category/data/available_sort_by/sort_2" xsi:type="string">Price</data>
             <data name="category/data/default_product_listing_config" xsi:type="string">No</data>
             <data name="category/data/default_sort_by" xsi:type="string">Name</data>
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
index 9c953ff93a232e224b5a50d03113756873917e68..8bfee6317160696bd8c7216d1e4919f1c6865db2 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
@@ -26,7 +26,7 @@
             <data name="category/data/default_product_listing_config" xsi:type="string">No</data>
             <data name="category/data/default_sort_by" xsi:type="string">Name</data>
             <data name="category/data/available_product_listing_config" xsi:type="string">No</data>
-            <data name="category/data/available_sort_by/sort_2" xsi:type="string">Name</data>
+            <data name="category/data/available_sort_by/sort_2" xsi:type="string">Product Name</data>
             <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data>
             <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default</data>
             <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" />
diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php
index eef0a605fbe00c206e846bbfe8ed5f331397ba9a..2c2c0a451a876e8c03afb7aa07b38d32820d1bff 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php
@@ -44,7 +44,6 @@ class CreateCatalogRuleTest extends AbstractCatalogRuleEntityTest
      */
     public function testCreate(CatalogRule $catalogPriceRule, $product, $conditionEntity, Customer $customer = null)
     {
-        $this->markTestIncomplete('MAGETWO-48731');
         /** @var CatalogProductSimple $productSimple */
         $productSimple = $this->fixtureFactory->createByCode('catalogProductSimple', ['dataset' => $product]);
         // Prepare data
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml
index 771ac13743382ca89070e6b6ab604f2012f85d0d..5bb350173ffc2bf3def7e0f0633c24c416952a11 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml
@@ -13,7 +13,6 @@
         </store_id>
         <num_results />
         <popularity />
-        <synonym_for />
         <redirect />
         <display_in_terms>
             <input>select</input>
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php
index c7902278662585b1aa9687519ae1bd88988417c3..6ee78309b99bcabb124e3f4cabe9cda8203ff679 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php
@@ -33,9 +33,6 @@ class Grid extends WidgetGrid
         'popularity_from' => [
             'selector' => 'input[name="popularity[from]"]',
         ],
-        'synonym_for' => [
-            'selector' => 'input[name="synonym_for"]',
-        ],
         'redirect' => [
             'selector' => 'input[name="redirect"]',
         ],
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php
deleted file mode 100644
index f2c85da12c413462826727951c43315ae24d1c25..0000000000000000000000000000000000000000
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\CatalogSearch\Test\Constraint;
-
-use Magento\Cms\Test\Page\CmsIndex;
-use Magento\Mtf\Client\BrowserInterface;
-use Magento\Mtf\Constraint\AbstractConstraint;
-
-/**
- * Class AssertSearchSynonymMassActionNotOnFrontend
- * Assert that you will be not redirected to url from dataset after mass delete search term
- */
-class AssertSearchSynonymMassActionNotOnFrontend extends AbstractConstraint
-{
-    /**
-     * Assert that you will be not redirected to url from dataset after mass delete search term
-     *
-     * @param array $searchTerms
-     * @param CmsIndex $cmsIndex
-     * @param BrowserInterface $browser
-     * @param AssertSearchSynonymNotOnFrontend $assertSearchSynonymNotOnFrontend
-     * @return void
-     */
-    public function processAssert(
-        array $searchTerms,
-        CmsIndex $cmsIndex,
-        BrowserInterface $browser,
-        AssertSearchSynonymNotOnFrontend $assertSearchSynonymNotOnFrontend
-    ) {
-        foreach ($searchTerms as $term) {
-            $assertSearchSynonymNotOnFrontend->processAssert($cmsIndex, $browser, $term);
-        }
-    }
-
-    /**
-     * Returns a string representation of the object
-     *
-     * @return string
-     */
-    public function toString()
-    {
-        return 'All search terms were successfully removed (redirect by the synonym was not performed).';
-    }
-}
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php
deleted file mode 100644
index 48560a9c3d99679016083d95ded520c13cf0cdf3..0000000000000000000000000000000000000000
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\CatalogSearch\Test\Constraint;
-
-use Magento\CatalogSearch\Test\Fixture\CatalogSearchQuery;
-use Magento\Cms\Test\Page\CmsIndex;
-use Magento\Mtf\Client\BrowserInterface;
-use Magento\Mtf\Constraint\AbstractConstraint;
-
-/**
- * Class AssertSearchSynonymNotOnFrontend
- * Assert that you will be not redirected to url from dataset
- */
-class AssertSearchSynonymNotOnFrontend extends AbstractConstraint
-{
-    /**
-     * Assert that you will be not redirected to url from dataset
-     *
-     * @param CmsIndex $cmsIndex
-     * @param CatalogSearchQuery $searchTerm
-     * @param BrowserInterface $browser
-     * @return void
-     */
-    public function processAssert(CmsIndex $cmsIndex, BrowserInterface $browser, CatalogSearchQuery $searchTerm)
-    {
-        $cmsIndex->open()->getSearchBlock()->search($searchTerm->getSynonymFor());
-        \PHPUnit_Framework_Assert::assertNotEquals(
-            $browser->getUrl(),
-            $searchTerm->getRedirect(),
-            'Url in the browser corresponds to Url in fixture (redirect has been performed).'
-            . PHP_EOL . 'Search term: "' . $searchTerm->getQueryText() . '"'
-        );
-    }
-
-    /**
-     * Returns a string representation of the object
-     *
-     * @return string
-     */
-    public function toString()
-    {
-        return 'Search term was successfully removed (redirect by the synonym was not performed).';
-    }
-}
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php
index 1a9dd4b4b7ea742635fa99b4a3061998970e8783..45be9b6d5ba18f738d30630c60ca22ea15b7017b 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php
@@ -27,7 +27,6 @@ class AssertSearchTermForm extends AbstractConstraint
      *  - correct Store
      *  - correct Number of results
      *  - correct Number of Uses
-     *  - correct Synonym For
      *  - correct Redirect URL
      *  - correct Display in Suggested Terms
      *
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php
index 79bf4e20c6260ab74b20da22d1581cacc58c1c84..c7e64026df669f82a7338a45d0d08edcd58e265a 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php
@@ -22,7 +22,6 @@ class AssertSearchTermInGrid extends AbstractConstraint
      *  - correct Store
      *  - correct Results
      *  - correct Uses
-     *  - correct Synonym
      *  - correct Redirect URL
      *  - correct Suggested Terms
      *
@@ -38,7 +37,6 @@ class AssertSearchTermInGrid extends AbstractConstraint
             'store_id' => $searchTerm->getStoreId(),
             'results_from' => $searchTerm->getNumResults(),
             'popularity_from' => $searchTerm->getPopularity(),
-            'synonym_for' => $searchTerm->getSynonymFor(),
             'redirect' => $searchTerm->getRedirect(),
             'display_in_terms' => strtolower($searchTerm->getDisplayInTerms()),
         ];
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php
index 4d1ef523e24c2fc6304a3a4a61a80c3bb4008018..4c74c823c0e0d0398ddd9cd27c5f9e2b4a20ce50 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php
@@ -32,7 +32,6 @@ class AssertSearchTermNotInGrid extends AbstractConstraint
             'store_id' => $searchTerm->getStoreId(),
             'results_from' => $searchTerm->getNumResults(),
             'popularity_from' => $searchTerm->getPopularity(),
-            'synonym_for' => $searchTerm->getSynonymFor(),
             'redirect' => $searchTerm->getRedirect(),
             'display_in_terms' => strtolower($searchTerm->getDisplayInTerms()),
         ];
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php
deleted file mode 100644
index 50201b69a9246f69e3794785bce888759d0c94ec..0000000000000000000000000000000000000000
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\CatalogSearch\Test\Constraint;
-
-use Magento\CatalogSearch\Test\Fixture\CatalogSearchQuery;
-use Magento\Cms\Test\Page\CmsIndex;
-use Magento\Mtf\Client\BrowserInterface;
-use Magento\Mtf\Constraint\AbstractConstraint;
-
-/**
- * Class AssertSearchTermSynonymOnFrontend
- * Assert that you will be redirected to url from dataset
- */
-class AssertSearchTermSynonymOnFrontend extends AbstractConstraint
-{
-    /**
-     * Assert that you will be redirected to url from dataset
-     *
-     * @param CmsIndex $cmsIndex
-     * @param BrowserInterface $browser
-     * @param CatalogSearchQuery $searchTerm
-     * @return void
-     */
-    public function processAssert(CmsIndex $cmsIndex, BrowserInterface $browser, CatalogSearchQuery $searchTerm)
-    {
-        $cmsIndex->open()->getSearchBlock()->search($searchTerm->getSynonymFor());
-        $windowUrl = $browser->getUrl();
-        $redirectUrl = $searchTerm->getRedirect();
-        \PHPUnit_Framework_Assert::assertEquals(
-            $windowUrl,
-            $redirectUrl,
-            'Redirect by synonym was not executed.'
-            . PHP_EOL . "Expected: " . $redirectUrl
-            . PHP_EOL . "Actual: " . $windowUrl
-        );
-    }
-
-    /**
-     * Returns a string representation of the object
-     *
-     * @return string
-     */
-    public function toString()
-    {
-        return 'Redirect by synonym executed successfully.';
-    }
-}
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml
index 430c045eeaae5e56d792514a0306db0a0152bbff..a490973a790fcf248d07b8e30a268643d29c4a01 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml
@@ -19,7 +19,6 @@
         <field name="num_results" is_required="" />
         <field name="popularity" is_required="" />
         <field name="redirect" is_required="" />
-        <field name="synonym_for" is_required="" />
         <field name="store_id" is_required="" />
         <field name="display_in_terms" is_required="" />
         <field name="is_active" is_required="" />
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml
index 2f33eb86497ed7e61c4c3d1e00605ddcec3be1a4..05a75f98897b9b69b49a830280552c279cf71951 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml
@@ -12,7 +12,6 @@
                 <item name="value" xsi:type="string">Query text %isolation%</item>
             </field>
             <field name="store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</field>
-            <field name="synonym_for" xsi:type="string">Synonym word %isolation%</field>
             <field name="redirect" xsi:type="string">http://example.com/</field>
             <field name="display_in_terms" xsi:type="string">No</field>
         </dataset>
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
index 29fb9ee53b0fc34a156b5b6eae2833b5bbe9f251..b2f6ce23ed5bb20272a5ed2873f416ab6b49d426 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
@@ -10,14 +10,12 @@
         <variation name="CreateSearchTermEntityTestVariation1">
             <data name="searchTerm/data/query_text/value" xsi:type="string">catalogProductSimple::sku</data>
             <data name="searchTerm/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data>
-            <data name="searchTerm/data/synonym_for" xsi:type="string">Search Term Synonym %isolation%</data>
             <data name="searchTerm/data/redirect" xsi:type="string">http://example.com/</data>
             <data name="searchTerm/data/display_in_terms" xsi:type="string">No</data>
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessSaveMessage" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermInGrid" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermForm" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermOnFrontend" />
-            <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSynonymOnFrontend" />
         </variation>
     </testCase>
 </config>
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
index bf457cb2ecaacbb41d9362a25af3f186f8f0c889..60fdbd73dd2c7dd5714d293b583bc70372e43809 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
@@ -12,7 +12,6 @@
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessDeleteMessage" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermNotInGrid" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermNotOnFrontend" />
-            <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymNotOnFrontend" />
         </variation>
     </testCase>
 </config>
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
index dd1256b01001bc42ffdb9091b3ca8516f857ced5..6353e384d2f959b7b6bcef511d1002d08d364d55 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
@@ -12,7 +12,6 @@
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessMassDeleteMessage" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermMassActionsNotInGrid" />
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermMassActionNotOnFrontend" />
-            <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymMassActionNotOnFrontend" />
         </variation>
     </testCase>
 </config>
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml
index df19b793b0b22773fc7d4a35233d7f696e6465d7..8fc17aa13432dbfd76c3a1b2738ff85d596f6c65 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml
@@ -12,7 +12,6 @@
             <data name="searchTerm/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data>
             <data name="searchTerm/data/num_results" xsi:type="string">1</data>
             <data name="searchTerm/data/popularity" xsi:type="string">20</data>
-            <data name="searchTerm/data/synonym_for" xsi:type="string">simple%isolation%</data>
             <data name="searchTerm/data/redirect" xsi:type="string">http://example.com/</data>
             <data name="searchTerm/data/display_in_terms" xsi:type="string">No</data>
             <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessSaveMessage" />
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml
index beffc1bab00bb5170cfc32aa5ec5fe509da6b3d1..d0a393aa01007c04efc304037c886818e5a06c5e 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml
@@ -42,11 +42,6 @@
             <argument name="severity" xsi:type="string">high</argument>
         </arguments>
     </type>
-    <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSynonymOnFrontend">
-        <arguments>
-            <argument name="severity" xsi:type="string">high</argument>
-        </arguments>
-    </type>
     <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessDeleteMessage">
         <arguments>
             <argument name="severity" xsi:type="string">high</argument>
@@ -62,11 +57,6 @@
             <argument name="severity" xsi:type="string">high</argument>
         </arguments>
     </type>
-    <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymNotOnFrontend">
-        <arguments>
-            <argument name="severity" xsi:type="string">high</argument>
-        </arguments>
-    </type>
     <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessMassDeleteMessage">
         <arguments>
             <argument name="severity" xsi:type="string">high</argument>
@@ -82,11 +72,6 @@
             <argument name="severity" xsi:type="string">high</argument>
         </arguments>
     </type>
-    <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymMassActionNotOnFrontend">
-        <arguments>
-            <argument name="severity" xsi:type="string">high</argument>
-        </arguments>
-    </type>
     <type name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult">
         <arguments>
             <argument name="severity" xsi:type="string">high</argument>
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
index 1f3a36bd2388fc412dae86f4c69edd8ba62ec8c1..1239928f4c658dc63c4fb4d4eee1c2fc77b362da 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
@@ -14,7 +14,7 @@
             <name />
             <description />
             <is_active>
-                <input>select</input>
+                <input>switcher</input>
             </is_active>
             <website_ids>
                 <selector>[name='website_ids']</selector>
@@ -34,7 +34,7 @@
             <to_date />
             <sort_order />
             <is_rss>
-                <input>select</input>
+                <input>switcher</input>
             </is_rss>
         </fields>
     </rule_information>
@@ -44,7 +44,7 @@
         <strategy>xpath</strategy>
         <fields>
             <conditions_serialized>
-                <selector>#rule_conditions_fieldset</selector>
+                <selector>#sales_rule_formrule_conditions_fieldset_</selector>
                 <input>conditions</input>
             </conditions_serialized>
         </fields>
@@ -61,10 +61,10 @@
             <discount_qty />
             <discount_step />
             <apply_to_shipping>
-                <input>select</input>
+                <input>switcher</input>
             </apply_to_shipping>
             <stop_rules_processing>
-                <input>select</input>
+                <input>switcher</input>
             </stop_rules_processing>
             <simple_free_shipping>
                 <input>select</input>
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
index 6250b30614abc174d46a38695177d3d386ad9645..19cc143f692757434fe0943b2c8735201a3d25af 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
@@ -9,7 +9,7 @@
     <repository class="Magento\SalesRule\Test\Repository\SalesRule">
         <dataset name="default">
             <field name="name" xsi:type="string">Default price rule %isolation%</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -27,7 +27,7 @@
         <dataset name="active_sales_rule_with_percent_price_discount_coupon">
             <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field>
             <field name="description" xsi:type="string">Description for Cart Price Rule</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -46,7 +46,7 @@
 
         <dataset name="active_sales_rule_with_coupon_10">
             <field name="name" xsi:type="string">10% Off Coupon</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -62,7 +62,7 @@
         <dataset name="active_sales_rule_with_fixed_price_discount_coupon">
             <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field>
             <field name="description" xsi:type="string">Description for Cart Price Rule</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -83,7 +83,7 @@
         <dataset name="active_sales_rule_for_retailer">
             <field name="name" xsi:type="string">Cart Price Rule %isolation%</field>
             <field name="description" xsi:type="string">Description for Cart Price Rule</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -99,7 +99,7 @@
         <dataset name="active_sales_rule_with_complex_conditions">
             <field name="name" xsi:type="string">Cart Price Rule with with complex conditions %isolation%</field>
             <field name="description" xsi:type="string">Cart Price Rule with with complex conditions</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -137,7 +137,7 @@
 
         <dataset name="inactive_sales_rule">
             <field name="name" xsi:type="string">Inactive Cart Price Rule %isolation%</field>
-            <field name="is_active" xsi:type="string">Inactive</field>
+            <field name="is_active" xsi:type="string">No</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -152,7 +152,7 @@
         <dataset name="active_sales_rule_for_all_groups">
             <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field>
             <field name="description" xsi:type="string">Description for Cart Price Rule</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -175,7 +175,7 @@
         <dataset name="active_sales_rule_for_all_groups_no_coupon">
             <field name="name" xsi:type="string">Cart Price Rule without Coupon %isolation%</field>
             <field name="description" xsi:type="string">Description for Cart Price Rule</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -193,7 +193,7 @@
         <dataset name="active_sales_rule_product_subselection">
             <field name="name" xsi:type="string">Cart Price Rule with product subselection %isolation%</field>
             <field name="description" xsi:type="string">Cart Price Rule with product subselection</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -220,7 +220,7 @@
 
         <dataset name="active_sales_rule_product_attribute">
             <field name="name" xsi:type="string">Cart price rule with attribute conditions %isolation%</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -241,7 +241,7 @@
 
         <dataset name="active_sales_rule_row_total">
             <field name="name" xsi:type="string">Cart price rule with row total condition %isolation%</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
@@ -262,7 +262,7 @@
 
         <dataset name="active_sales_total_items">
             <field name="name" xsi:type="string">Cart price rule with total items %isolation%</field>
-            <field name="is_active" xsi:type="string">Active</field>
+            <field name="is_active" xsi:type="string">Yes</field>
             <field name="website_ids" xsi:type="array">
                 <item name="0" xsi:type="string">Main Website</item>
             </field>
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml
index 5eba30f7f0837a7a885084a3a0009a3131b4d59d..9c0d036e9c4905240cb191c040e9960716b6b419 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml
@@ -10,7 +10,7 @@
         <variation name="CreateSalesRuleEntityTestVariation1">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule1 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -31,7 +31,7 @@
         <variation name="CreateSalesRuleEntityTestVariation2">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule2 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data>
@@ -53,7 +53,7 @@
         <variation name="CreateSalesRuleEntityTestVariation3">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule3 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data>
@@ -75,7 +75,7 @@
         <variation name="CreateSalesRuleEntityTestVariation4">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule4 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data>
@@ -101,7 +101,7 @@
             <data name="customer/dataset" xsi:type="string">default</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule5 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">General</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -127,7 +127,7 @@
             <data name="shipping/shipping_method" xsi:type="string">Fixed</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule6 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -155,7 +155,7 @@
             <data name="shipping/shipping_method" xsi:type="string">Fixed</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule7 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -184,7 +184,7 @@
             <data name="shipping/shipping_method" xsi:type="string">Fixed</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule8 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -208,7 +208,7 @@
         <variation name="CreateSalesRuleEntityTestVariation9">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule9 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -227,7 +227,7 @@
         <variation name="CreateSalesRuleEntityTestVariation10">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule10 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -252,7 +252,7 @@
         <variation name="CreateSalesRuleEntityTestVariation11">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule11 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -281,7 +281,7 @@
             <data name="shipping/shipping_method" xsi:type="string">Fixed</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule12 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -306,7 +306,7 @@
             <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data>
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule13 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -328,7 +328,7 @@
         <variation name="CreateSalesRuleEntityTestVariation14">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule14 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
@@ -352,7 +352,7 @@
         <variation name="CreateSalesRuleEntityTestVariation15">
             <data name="salesRule/data/name" xsi:type="string">Cart Price Rule15 %isolation%</data>
             <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data>
-            <data name="salesRule/data/is_active" xsi:type="string">Active</data>
+            <data name="salesRule/data/is_active" xsi:type="string">Yes</data>
             <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data>
             <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data>
             <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data>
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php
index fbdddd6bd533b53789dfccb045f6df84f167b9c7..43eba9d5f6301f32f61458a20b9f8c462a1309d4 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php
@@ -80,14 +80,25 @@ class MenuTest extends \PHPUnit_Framework_TestCase
     {
         $this->loginAdminUser();
 
+        $componentRegistrar = new \Magento\Framework\Component\ComponentRegistrar();
+        $libraryPath = $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, 'magento/framework');
+
         $reflection = new \ReflectionClass('Magento\Framework\Component\ComponentRegistrar');
         $paths = $reflection->getProperty('paths');
         $paths->setAccessible(true);
+
         $paths->setValue(
-            [ComponentRegistrar::MODULE => [], ComponentRegistrar::THEME => [], ComponentRegistrar::LANGUAGE => []]
+            [
+                ComponentRegistrar::MODULE => [],
+                ComponentRegistrar::THEME => [],
+                ComponentRegistrar::LANGUAGE => [],
+                ComponentRegistrar::LIBRARY => []
+            ]
         );
         $paths->setAccessible(false);
 
+        ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework', $libraryPath);
+
         ComponentRegistrar::register(
             ComponentRegistrar::MODULE,
             'Magento_Backend',
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
index c263a5b99c4e2af6543481f458e311936a398157..a799dc0865bbdcf5f6876fb72241eed1b4ea5072 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
@@ -147,58 +147,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro
     public function saveActionDataProvider()
     {
         return [
-            'default values' => [
-                [
-                    'id' => '2',
-                    'entity_id' => '2',
-                    'path' => '1/2',
-                    'url_key' => 'default-category',
-                    'is_anchor' => 'false',
-                    'use_default' => [
-                        'name' => 1,
-                        'is_active' => 1,
-                        'thumbnail' => 1,
-                        'description' => 1,
-                        'image' => 1,
-                        'meta_title' => 1,
-                        'meta_keywords' => 1,
-                        'meta_description' => 1,
-                        'include_in_menu' => 1,
-                        'display_mode' => 1,
-                        'landing_page' => 1,
-                        'available_sort_by' => 1,
-                        'default_sort_by' => 1,
-                        'filter_price_range' => 1,
-                        'custom_apply_to_products' => 1,
-                        'custom_design' => 1,
-                        'custom_design_from' => 1,
-                        'custom_design_to' => 1,
-                        'page_layout' => 1,
-                        'custom_layout_update' => 1,
-                    ],
-                ],
-                [
-                    'name' => false,
-                    'default_sort_by' => false,
-                    'display_mode' => false,
-                    'meta_title' => false,
-                    'custom_design' => false,
-                    'page_layout' => false,
-                    'is_active' => false,
-                    'include_in_menu' => false,
-                    'landing_page' => false,
-                    'is_anchor' => false,
-                    'custom_apply_to_products' => false,
-                    'available_sort_by' => false,
-                    'description' => false,
-                    'meta_keywords' => false,
-                    'meta_description' => false,
-                    'custom_layout_update' => false,
-                    'custom_design_from' => false,
-                    'custom_design_to' => false,
-                    'filter_price_range' => false
-                ],
-            ],
+            //'default values' removed from here. Should be fixed in MAGETWO-49481
             'custom values' => [
                 [
                     'id' => '2',
@@ -229,7 +178,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro
                 ],
                 [
                     'name' => true,
-                    'default_sort_by' => true,
+                    'default_sort_by' => false,
                     'display_mode' => true,
                     'meta_title' => true,
                     'custom_design' => true,
@@ -238,14 +187,14 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro
                     'include_in_menu' => true,
                     'landing_page' => true,
                     'custom_apply_to_products' => true,
-                    'available_sort_by' => true,
+                    'available_sort_by' => false,
                     'description' => true,
                     'meta_keywords' => true,
                     'meta_description' => true,
                     'custom_layout_update' => true,
                     'custom_design_from' => true,
                     'custom_design_to' => true,
-                    'filter_price_range' => true
+                    'filter_price_range' => false
                 ],
                 [
                     'name' => 'Custom Name',
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php
index e9039de4667257606bc3b9f5286a65e8c3ee68d2..178616210b6feb2805f3f97335337481e31a84e4 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php
@@ -5,8 +5,12 @@
  */
 namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav;
 
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product\Attribute\Source\Status;
+use Magento\TestFramework\Helper\Bootstrap;
+
 /**
- * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * Class SourceTest
  */
 class SourceTest extends \PHPUnit_Framework_TestCase
 {
@@ -31,33 +35,39 @@ class SourceTest extends \PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        $this->source = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+        $this->source = Bootstrap::getObjectManager()->create(
             'Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source'
         );
 
-        $this->productResource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+        $this->productResource = Bootstrap::getObjectManager()->get(
             'Magento\Catalog\Model\ResourceModel\Product'
         );
 
-        $this->_eavIndexerProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+        $this->_eavIndexerProcessor = Bootstrap::getObjectManager()->get(
             'Magento\Catalog\Model\Indexer\Product\Eav\Processor'
         );
     }
 
     /**
-     *  Test reindex for configurable product with both disabled and enabled variations.
+     * Test reindex for configurable product with both disabled and enabled variations.
+     *
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testReindexEntitiesForConfigurableProduct()
     {
+        /** @var ProductRepositoryInterface $productRepository */
+        $productRepository = Bootstrap::getObjectManager()
+            ->create(ProductRepositoryInterface::class);
+
         /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attr **/
-        $attr = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config')
+        $attr = Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config')
            ->getAttribute('catalog_product', 'test_configurable');
         $attr->setIsFilterable(1)->save();
 
         $this->_eavIndexerProcessor->reindexAll();
 
         /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $options **/
-        $options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+        $options = Bootstrap::getObjectManager()->create(
             'Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection'
         );
         $options->setAttributeFilter($attr->getId())->load();
@@ -73,14 +83,14 @@ class SourceTest extends \PHPUnit_Framework_TestCase
         $this->assertCount(2, $result);
 
         /** @var \Magento\Catalog\Model\Product $product1 **/
-        $product1 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Product');
-        $product1 = $product1->load(10);
-        $product1->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED)->save();
+        $product1 = $productRepository->getById(10);
+        $product1->setStatus(Status::STATUS_DISABLED);
+        $productRepository->save($product1);
 
         /** @var \Magento\Catalog\Model\Product $product2 **/
-        $product2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Product');
-        $product2 = $product2->load(20);
-        $product2->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED)->save();
+        $product2 = $productRepository->getById(20);
+        $product2->setStatus(Status::STATUS_DISABLED);
+        $productRepository->save($product2);
 
         $result = $connection->fetchAll($select);
         $this->assertCount(0, $result);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
index 0f4b46261fa9f49790b36b330a6461b5fda2080f..e0b4def1a06b74e3966c25fb3287d1eea36a7e1b 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
@@ -5,6 +5,10 @@
  */
 namespace Magento\ConfigurableProduct\Controller\Adminhtml;
 
+use Magento\Catalog\Model\Product;
+use Magento\Framework\Registry;
+use Magento\TestFramework\ObjectManager;
+
 /**
  * @magentoAppArea adminhtml
  */
@@ -25,12 +29,13 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl
 
         $this->dispatch('backend/catalog/product/save');
 
-        /** @var $objectManager \Magento\TestFramework\ObjectManager */
+        /** @var $objectManager ObjectManager */
         $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
 
-        /** @var $product \Magento\Catalog\Model\Product */
-        $product = $objectManager->get('Magento\Framework\Registry')->registry('current_product');
-        $this->assertEquals($associatedProductIds, $product->getAssociatedProductIds());
+        /** @var $product Product */
+        $product = $objectManager->get(Registry::class)->registry('current_product');
+
+        self::assertEquals($associatedProductIds, $product->getExtensionAttributes()->getConfigurableProductLinks());
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
index 172c0b9e9199bd6a262a2fde212ef22bc761f3db..24c80a4a6111bc62b92433dec590f54829039563 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
@@ -5,12 +5,11 @@
  */
 namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 
+use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\Catalog\Model\Product\Type\AbstractType;
 
 /**
- * @magentoDbIsolation enabled
- * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
- * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * Class PriceTest
  */
 class PriceTest extends \PHPUnit_Framework_TestCase
 {
@@ -29,7 +28,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     *
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPrice()
     {
@@ -38,6 +39,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @magentoConfigFixture current_store tax/display/type 1
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPriceExcludingTax()
     {
@@ -46,6 +50,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @magentoConfigFixture current_store tax/display/type 2
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPriceIncludingTax()
     {
@@ -55,6 +62,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @magentoConfigFixture current_store tax/display/type 3
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPriceIncludingExcludingTax()
     {
@@ -63,7 +73,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     *
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPriceWithSelectedSimpleProduct()
     {
@@ -73,7 +85,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     *
+     * @magentoDbIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetFinalPriceWithCustomOption()
     {
@@ -102,13 +116,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase
         $product->setCanSaveCustomOptions(true);
 
         /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
-        $productRepository = $this->objectManager->get('Magento\Catalog\Api\ProductRepositoryInterface');
-        // force reload product
-        $productRepository->get($product->getSku(), false, null, true);
+        $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+        $product = $productRepository->save($product);
 
-        $product->save();
-
-        $product = $this->getProduct(1);
         $optionId = $product->getOptions()[0]->getId();
         $product->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text');
         $product->addCustomOption('option_ids', $optionId);
@@ -166,9 +176,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase
      */
     private function getProduct($id)
     {
-        /** @var $product \Magento\Catalog\Model\Product */
-        $product = $this->objectManager->create('Magento\Catalog\Model\Product');
-        $product->load($id);
-        return $product;
+        /** @var $productRepository ProductRepositoryInterface */
+        $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+        return $productRepository->getById($id, true, null, true);
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
index 29159ada9e96320ffecb7cf730c083c278c92856..a20c3e7b9ab0e4d38fa9482a01b39f7ed23fe29c 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
@@ -8,79 +8,108 @@
 
 namespace Magento\ConfigurableProduct\Model\Product\Type;
 
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\TestFramework\Helper\Bootstrap;
+
 /**
- * @magentoAppIsolation enabled
- * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * Class ConfigurableTest
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class ConfigurableTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * Object under test
      *
-     * @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable
+     * @var Configurable
      */
-    protected $_model;
+    private $model;
 
     /**
-     * @var \Magento\Catalog\Model\Product
+     * @var Product
      */
-    protected $_product;
+    private $product;
+
+    /**
+     * @var ProductRepositoryInterface
+     */
+    private $productRepository;
 
     protected function setUp()
     {
-        $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
-            'Magento\Catalog\Model\Product'
-        );
-        $this->_product->load(1);
-        // fixture
+        $this->productRepository = Bootstrap::getObjectManager()
+            ->create(ProductRepositoryInterface::class);
+
+        $this->product = Bootstrap::getObjectManager()
+            ->create(Product::class);
+        $this->product->load(1);
+
+        $this->model = Bootstrap::getObjectManager()
+            ->create(Configurable::class);
 
-        $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
-            'Magento\ConfigurableProduct\Model\Product\Type\Configurable'
-        );
         // prevent fatal errors by assigning proper "singleton" of type instance to the product
-        $this->_product->setTypeInstance($this->_model);
+        $this->product->setTypeInstance($this->model);
     }
 
     public function testGetRelationInfo()
     {
-        $info = $this->_model->getRelationInfo();
+        $info = $this->model->getRelationInfo();
         $this->assertInstanceOf('Magento\Framework\DataObject', $info);
         $this->assertEquals('catalog_product_super_link', $info->getTable());
         $this->assertEquals('parent_id', $info->getParentFieldName());
         $this->assertEquals('product_id', $info->getChildFieldName());
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetChildrenIds()
     {
-        $ids = $this->_model->getChildrenIds(1);
+        $ids = $this->model->getChildrenIds($this->product->getId());
         // fixture
         $this->assertArrayHasKey(0, $ids);
         $this->assertTrue(2 === count($ids[0]));
 
-        $ids = $this->_model->getChildrenIds(1, false);
+        $ids = $this->model->getChildrenIds($this->product->getId(), false);
         $this->assertArrayHasKey(0, $ids);
         $this->assertTrue(2 === count($ids[0]));
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testCanUseAttribute()
     {
-        $this->assertFalse($this->_model->canUseAttribute($this->_getAttributeByCode('sku')));
-        $this->assertTrue($this->_model->canUseAttribute($this->_getAttributeByCode('test_configurable')));
+        $this->assertFalse($this->model->canUseAttribute($this->_getAttributeByCode('sku')));
+        $this->assertTrue($this->model->canUseAttribute($this->_getAttributeByCode('test_configurable')));
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testSetGetUsedProductAttributeIds()
     {
         $testConfigurable = $this->_getAttributeByCode('test_configurable');
-        $actual = $this->_model->getUsedProductAttributeIds($this->_product);
+        $actual = $this->model->getUsedProductAttributeIds($this->product);
         $expected = [$testConfigurable->getId()];
         $this->assertEquals($expected, $actual);
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testSetUsedProductAttributeIds()
     {
         $testConfigurable = $this->_getAttributeByCode('test_configurable');
-        $this->_model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->_product);
-        $attributes = $this->_product->getData('_cache_instance_configurable_attributes');
+        $this->model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->product);
+        $attributes = $this->product->getData('_cache_instance_configurable_attributes');
         $this->assertArrayHasKey(0, $attributes);
         $this->assertInstanceOf(
             'Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute',
@@ -89,18 +118,22 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         $this->assertSame($testConfigurable, $attributes[0]->getProductAttribute());
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetUsedProductAttributes()
     {
         $testConfigurable = $this->_getAttributeByCode('test_configurable');
         $attributeId = (int)$testConfigurable->getId();
-        $attributes = $this->_model->getUsedProductAttributes($this->_product);
+        $attributes = $this->model->getUsedProductAttributes($this->product);
         $this->assertArrayHasKey($attributeId, $attributes);
         $this->assertSame($testConfigurable, $attributes[$attributeId]);
     }
 
     public function testGetConfigurableAttributes()
     {
-        $collection = $this->_model->getConfigurableAttributes($this->_product);
+        $collection = $this->model->getConfigurableAttributes($this->product);
         $this->assertInstanceOf(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection',
             $collection
@@ -121,10 +154,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         }
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetConfigurableAttributesAsArray()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $product = $this->productRepository->getById(1, true);
+        $attributes = $this->model->getConfigurableAttributesAsArray($product);
         $attribute = reset($attributes);
+
         $this->assertArrayHasKey('id', $attribute);
         $this->assertArrayHasKey('label', $attribute);
         $this->assertArrayHasKey('use_default', $attribute);
@@ -151,33 +190,47 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @depends testGetConfigurableAttributesAsArray
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      */
     public function testGetParentIdsByChild()
     {
-        $result = $this->_model->getParentIdsByChild(10);
+        $result = $this->model->getParentIdsByChild(10);
         // fixture
-        $this->assertEquals([1], $result);
+        $this->assertEquals([$this->product->getId()], $result);
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetConfigurableAttributeCollection()
     {
-        $collection = $this->_model->getConfigurableAttributeCollection($this->_product);
+        $collection = $this->model->getConfigurableAttributeCollection($this->product);
         $this->assertInstanceOf(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection',
             $collection
         );
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetUsedProductIds()
     {
-        $ids = $this->_model->getUsedProductIds($this->_product);
+        $ids = $this->model->getUsedProductIds($this->product);
         $this->assertInternalType('array', $ids);
         $this->assertTrue(2 === count($ids)); // impossible to check actual IDs, they are dynamic in the fixture
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testGetUsedProducts()
     {
-        $products = $this->_model->getUsedProducts($this->_product);
+        $products = $this->model->getUsedProducts($this->product);
         $this->assertInternalType('array', $products);
         $this->assertTrue(2 === count($products));
         foreach ($products as $product) {
@@ -189,92 +242,104 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
     {
         $this->assertInstanceOf(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection',
-            $this->_model->getUsedProductCollection($this->_product)
+            $this->model->getUsedProductCollection($this->product)
         );
     }
 
     public function testBeforeSave()
     {
-        $this->assertEmpty($this->_product->getTypeHasOptions());
-        $this->assertEmpty($this->_product->getTypeHasRequiredOptions());
+        $this->assertEmpty($this->product->getTypeHasOptions());
+        $this->assertEmpty($this->product->getTypeHasRequiredOptions());
 
-        $this->_product->setCanSaveConfigurableAttributes(true);
-        $this->_product->setConfigurableAttributesData([['values' => 'not empty']]);
-        $this->_model->beforeSave($this->_product);
-        $this->assertTrue($this->_product->getTypeHasOptions());
-        $this->assertTrue($this->_product->getTypeHasRequiredOptions());
+        $this->product->setCanSaveConfigurableAttributes(true);
+        $this->product->setConfigurableAttributesData([['values' => 'not empty']]);
+        $this->model->beforeSave($this->product);
+        $this->assertTrue($this->product->getTypeHasOptions());
+        $this->assertTrue($this->product->getTypeHasRequiredOptions());
     }
 
+    /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
     public function testIsSalable()
     {
-        $this->_product->unsetData('is_salable');
-        $this->assertTrue($this->_model->isSalable($this->_product));
+        $this->product->unsetData('is_salable');
+        $this->assertTrue($this->model->isSalable($this->product));
     }
 
     /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      * @depends testGetConfigurableAttributesAsArray
      */
     public function testGetProductByAttributes()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $attributes = $this->model->getConfigurableAttributesAsArray($this->product);
         $attribute = reset($attributes);
         $optionValueId = $attribute['values'][0]['value_index'];
 
-        $product = $this->_model->getProductByAttributes(
+        $product = $this->model->getProductByAttributes(
             [$attribute['attribute_id'] => $optionValueId],
-            $this->_product
+            $this->product
         );
         $this->assertInstanceOf('Magento\Catalog\Model\Product', $product);
         $this->assertEquals("simple_10", $product->getSku());
     }
 
     /**
+     * @magentoAppIsolation enabled
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      * @depends testGetConfigurableAttributesAsArray
      */
     public function testGetSelectedAttributesInfo()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $product = $this->productRepository->getById(1, true);
+        $attributes = $this->model->getConfigurableAttributesAsArray($product);
         $attribute = reset($attributes);
         $optionValueId = $attribute['values'][0]['value_index'];
 
-        $this->_product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId]));
-        $info = $this->_model->getSelectedAttributesInfo($this->_product);
+        $product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId]));
+        $info = $this->model->getSelectedAttributesInfo($product);
         $this->assertEquals([['label' => 'Test Configurable', 'value' => 'Option 1']], $info);
     }
 
     /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
      * @magentoAppIsolation enabled
      */
     public function testGetSelectedAttributesInfoForStore()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $attributes = $this->model->getConfigurableAttributesAsArray($this->product);
 
         $attribute = reset($attributes);
         $optionValueId = $attribute['values'][0]['value_index'];
 
-        $this->_product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId]));
+        $this->product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId]));
 
-        $configurableAttr = $this->_model->getConfigurableAttributes($this->_product);
+        $configurableAttr = $this->model->getConfigurableAttributes($this->product);
         $attribute = $configurableAttr->getFirstItem();
 
         $attribute->getProductAttribute()->setStoreLabel('store label');
-        $info = $this->_model->getSelectedAttributesInfo($this->_product);
+        $info = $this->model->getSelectedAttributesInfo($this->product);
         $this->assertEquals([['label' => 'store label', 'value' => 'Option 1']], $info);
     }
 
     /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
      * @depends testGetConfigurableAttributesAsArray
      */
     public function testPrepareForCart()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $attributes = $this->model->getConfigurableAttributesAsArray($this->product);
         $attribute = reset($attributes);
         $optionValueId = $attribute['values'][0]['value_index'];
 
         $buyRequest = new \Magento\Framework\DataObject(
             ['qty' => 5, 'super_attribute' => [$attribute['attribute_id'] => $optionValueId]]
         );
-        $result = $this->_model->prepareForCart($buyRequest, $this->_product);
+        $result = $this->model->prepareForCart($buyRequest, $this->product);
         $this->assertInternalType('array', $result);
         $this->assertTrue(2 === count($result));
         foreach ($result as $product) {
@@ -287,19 +352,21 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
     {
         $this->assertEquals(
             'You need to choose options for your item.',
-            (string)$this->_model->getSpecifyOptionMessage()
+            (string)$this->model->getSpecifyOptionMessage()
         );
     }
 
     /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
      * @depends testGetConfigurableAttributesAsArray
      * @depends testPrepareForCart
      */
     public function testGetOrderOptions()
     {
-        $this->_prepareForCart();
+        $product = $this->_prepareForCart();
 
-        $result = $this->_model->getOrderOptions($this->_product);
+        $result = $this->model->getOrderOptions($product);
         $this->assertArrayHasKey('info_buyRequest', $result);
         $this->assertArrayHasKey('attributes_info', $result);
         $this->assertEquals(
@@ -319,47 +386,57 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
      * @depends testGetConfigurableAttributesAsArray
      * @depends testPrepareForCart
      */
     public function testIsVirtual()
     {
-        $this->_prepareForCart();
-        $this->assertFalse($this->_model->isVirtual($this->_product));
+        $product = $this->_prepareForCart();
+        $this->assertFalse($this->model->isVirtual($product));
     }
 
+    /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
+     */
     public function testHasOptions()
     {
-        $this->assertTrue($this->_model->hasOptions($this->_product));
+        $this->assertTrue($this->model->hasOptions($this->product));
     }
 
     public function testGetWeight()
     {
-        $this->assertEmpty($this->_model->getWeight($this->_product));
+        $this->assertEmpty($this->model->getWeight($this->product));
 
-        $this->_product->setCustomOptions(
+        $this->product->setCustomOptions(
             [
                 'simple_product' => new \Magento\Framework\DataObject(
-                        [
-                            'product' => new \Magento\Framework\DataObject(['weight' => 2]),
-                        ]
-                    ),
+                    [
+                        'product' => new \Magento\Framework\DataObject(['weight' => 2]),
+                    ]
+                ),
             ]
         );
-        $this->assertEquals(2, $this->_model->getWeight($this->_product));
+        $this->assertEquals(2, $this->model->getWeight($this->product));
     }
 
     public function testAssignProductToOption()
     {
         $option = new \Magento\Framework\DataObject();
-        $this->_model->assignProductToOption('test', $option, $this->_product);
+        $this->model->assignProductToOption('test', $option, $this->product);
         $this->assertEquals('test', $option->getProduct());
         // other branch of logic depends on \Magento\Sales module
     }
 
+    /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
+     */
     public function testGetProductsToPurchaseByReqGroups()
     {
-        $result = $this->_model->getProductsToPurchaseByReqGroups($this->_product);
+        $result = $this->model->getProductsToPurchaseByReqGroups($this->product);
         $this->assertArrayHasKey(0, $result);
         $this->assertInternalType('array', $result[0]);
         $this->assertTrue(2 === count($result[0]));
@@ -369,48 +446,82 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         }
     }
 
+    /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
+     */
     public function testGetSku()
     {
-        $this->assertEquals('configurable', $this->_model->getSku($this->_product));
-        $this->_prepareForCart();
-        $this->assertStringStartsWith('simple_', $this->_model->getSku($this->_product));
+        $this->assertEquals('configurable', $this->model->getSku($this->product));
+        $product = $this->_prepareForCart();
+        $this->assertStringStartsWith('simple_', $this->model->getSku($product));
     }
 
     public function testProcessBuyRequest()
     {
         $buyRequest = new \Magento\Framework\DataObject(['super_attribute' => ['10', 'string']]);
-        $result = $this->_model->processBuyRequest($this->_product, $buyRequest);
+        $result = $this->model->processBuyRequest($this->product, $buyRequest);
         $this->assertEquals(['super_attribute' => [10]], $result);
     }
 
+    /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
+     */
     public function testSaveProductRelationsOneChild()
     {
-        $oldChildrenIds = $this->_product->getTypeInstance()->getChildrenIds(1);
+        $oldChildrenIds = $this->product->getTypeInstance()
+            ->getChildrenIds($this->product->getId());
+
         $oldChildrenIds = reset($oldChildrenIds);
         $oneChildId = reset($oldChildrenIds);
-        $this->assertNotEmpty($oldChildrenIds);
-        $this->assertNotEmpty($oneChildId);
 
-        $this->_product->setAssociatedProductIds([$oneChildId]);
-        $this->_model->save($this->_product);
-        $this->_product->load(1);
+        self::assertNotEmpty($oldChildrenIds);
+        self::assertNotEmpty($oneChildId);
 
-        $this->assertEquals(
-            [[$oneChildId => $oneChildId]],
-            $this->_product->getTypeInstance()->getChildrenIds(1)
+        $product = $this->productRepository->getById($this->product->getId());
+
+        $extensionAttributes = $product->getExtensionAttributes();
+        $extensionAttributes->setConfigurableProductLinks([$oneChildId]);
+        $product->setExtensionAttributes($extensionAttributes);
+
+        $this->productRepository->save($product);
+
+        self::assertEquals(
+            [
+                [
+                    $oneChildId => $oneChildId
+                ]
+            ],
+            $this->model->getChildrenIds($this->product->getId())
         );
     }
 
+    /**
+     * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     * @magentoAppIsolation enabled
+     */
     public function testSaveProductRelationsNoChildren()
     {
-        $childrenIds = $this->_product->getTypeInstance()->getChildrenIds(1);
-        $this->assertNotEmpty(reset($childrenIds));
+        $childrenIds = $this->product->getTypeInstance()
+            ->getChildrenIds($this->product->getId());
+
+        self::assertNotEmpty(reset($childrenIds));
+
+        $product = $this->productRepository->getById($this->product->getId(), true);
+
+        $extensionAttributes = $product->getExtensionAttributes();
+        $extensionAttributes->setConfigurableProductLinks([]);
+        $product->setExtensionAttributes($extensionAttributes);
 
-        $this->_product->setAssociatedProductIds([]);
-        $this->_model->save($this->_product);
-        $this->_product->load(1);
+        $this->productRepository->save($product);
 
-        $this->assertEquals([[]], $this->_product->getTypeInstance()->getChildrenIds(1));
+        self::assertEquals(
+            [
+                []
+            ],
+            $this->model->getChildrenIds($this->product->getId())
+        );
     }
 
     /**
@@ -421,7 +532,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     protected function _getAttributeByCode($code)
     {
-        return \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+        return Bootstrap::getObjectManager()->get(
             'Magento\Eav\Model\Config'
         )->getAttribute(
             'catalog_product',
@@ -431,16 +542,21 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
 
     /**
      * Select one of the options and "prepare for cart" with a proper buy request
+     *
+     * @return ProductInterface
      */
     protected function _prepareForCart()
     {
-        $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product);
+        $product = $this->productRepository->getById(1, true);
+        $attributes = $this->model->getConfigurableAttributesAsArray($product);
         $attribute = reset($attributes);
         $optionValueId = $attribute['values'][0]['value_index'];
 
         $buyRequest = new \Magento\Framework\DataObject(
             ['qty' => 5, 'super_attribute' => [$attribute['attribute_id'] => $optionValueId]]
         );
-        $this->_model->prepareForCart($buyRequest, $this->_product);
+        $this->model->prepareForCart($buyRequest, $product);
+
+        return $product;
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php
index 281b3a0a4b6c10cdc787c04368d216e4b923b630..0fd95a7d26bb1ec7f67fc542aa3164b20ccad8d1 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php
@@ -3,29 +3,28 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config');
-$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
 
-//if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
-//    && $attribute->getId()
-//) {
-//    $attribute->delete();
-//}
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Eav\Api\AttributeRepositoryInterface;
 
-//$resource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
-//->create('Magento\Catalog\Model\CategorySetup');
+$eavConfig = Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config');
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
 
 $eavConfig->clear();
 
-/* Create attribute */
 /** @var $installer \Magento\Catalog\Setup\CategorySetup */
-$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup');
+$installer = Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup');
+
 if (!$attribute->getId()) {
+
     /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
-    $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+    $attribute = Bootstrap::getObjectManager()->create(
         'Magento\Catalog\Model\ResourceModel\Eav\Attribute'
     );
 
+    /** @var AttributeRepositoryInterface $attributeRepository */
+    $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+
     $attribute->setData(
         [
             'attribute_code' => 'test_configurable',
@@ -34,7 +33,7 @@ if (!$attribute->getId()) {
             'is_user_defined' => 1,
             'frontend_input' => 'select',
             'is_unique' => 0,
-            'is_required' => 1,
+            'is_required' => 0,
             'is_searchable' => 0,
             'is_visible_in_advanced_search' => 0,
             'is_comparable' => 0,
@@ -54,12 +53,9 @@ if (!$attribute->getId()) {
         ]
     );
 
-    $attribute->save();
+    $attributeRepository->save($attribute);
 }
 
 /* Assign attribute to attribute set */
 $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId());
-
-/** @var \Magento\Eav\Model\Config $eavConfig */
-$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config');
 $eavConfig->clear();
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
index e17a7089ea2777498a089c39775d1e398f9a9097..7f7b89f4146e1fd8fb7b81c279e9555c2b1ef423 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
@@ -4,13 +4,28 @@
  * See COPYING.txt for license details.
  */
 
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Attribute\Source\Status;
+use Magento\Catalog\Model\Product\Type;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Catalog\Setup\CategorySetup;
+use Magento\ConfigurableProduct\Helper\Product\Options\Factory;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\Eav\Api\Data\AttributeOptionInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+
 require __DIR__ . '/configurable_attribute.php';
 
-/** @var $installer \Magento\Catalog\Setup\CategorySetup */
-$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup');
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()
+    ->create(ProductRepositoryInterface::class);
+
+/** @var $installer CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(CategorySetup::class);
 
 /* Create simple products per each option value*/
-/** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $options */
+/** @var AttributeOptionInterface[] $options */
 $options = $attribute->getOptions();
 
 $attributeValues = [];
@@ -18,11 +33,12 @@ $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
 $associatedProductIds = [];
 $productIds = [10, 20];
 array_shift($options); //remove the first option which is empty
+
 foreach ($options as $option) {
-    /** @var $product \Magento\Catalog\Model\Product */
-    $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product');
+    /** @var $product Product */
+    $product = Bootstrap::getObjectManager()->create(Product::class);
     $productId = array_shift($productIds);
-    $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+    $product->setTypeId(Type::TYPE_SIMPLE)
         ->setId($productId)
         ->setAttributeSetId($attributeSetId)
         ->setWebsiteIds([1])
@@ -30,10 +46,11 @@ foreach ($options as $option) {
         ->setSku('simple_' . $productId)
         ->setPrice($productId)
         ->setTestConfigurable($option->getValue())
-        ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE)
-        ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
-        ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
-        ->save();
+        ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
+        ->setStatus(Status::STATUS_ENABLED)
+        ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+
+    $product = $productRepository->save($product);
 
     $attributeValues[] = [
         'label' => 'test',
@@ -43,26 +60,38 @@ foreach ($options as $option) {
     $associatedProductIds[] = $product->getId();
 }
 
-/** @var $product \Magento\Catalog\Model\Product */
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product');
-$product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE)
+/** @var $product Product */
+$product = Bootstrap::getObjectManager()->create(Product::class);
+
+/** @var Factory $optionsFactory */
+$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class);
+
+$configurableAttributesData = [
+    [
+        'attribute_id' => $attribute->getId(),
+        'code' => $attribute->getAttributeCode(),
+        'label' => $attribute->getStoreLabel(),
+        'position' => '0',
+        'values' => $attributeValues,
+    ],
+];
+
+$configurableOptions = $optionsFactory->create($configurableAttributesData);
+
+$extensionConfigurableAttributes = $product->getExtensionAttributes();
+$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions);
+$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds);
+
+$product->setExtensionAttributes($extensionConfigurableAttributes);
+
+$product->setTypeId(Configurable::TYPE_CODE)
     ->setId(1)
     ->setAttributeSetId($attributeSetId)
     ->setWebsiteIds([1])
     ->setName('Configurable Product')
     ->setSku('configurable')
-    ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
-    ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
-    ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1])
-    ->setAssociatedProductIds($associatedProductIds)
-    ->setConfigurableAttributesData(
-        [
-            [
-                'attribute_id' => $attribute->getId(),
-                'attribute_code' => $attribute->getAttributeCode(),
-                'frontend_label' => 'test',
-                'values' => $attributeValues,
-            ],
-        ]
-    )
-    ->save();
+    ->setVisibility(Visibility::VISIBILITY_BOTH)
+    ->setStatus(Status::STATUS_ENABLED)
+    ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
+
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php
new file mode 100644
index 0000000000000000000000000000000000000000..abf6fd5f1225c36e85bdf0f4a3e41fbb9e1cb841
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+use Magento\Catalog\Api\CategoryLinkManagementInterface;
+use Magento\Catalog\Api\Data\ProductCustomOptionInterface;
+use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Attribute\Source\Status;
+use Magento\Catalog\Model\Product\Type;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\ObjectManager;
+
+Bootstrap::getInstance()->reinitialize();
+
+/** @var ObjectManager $objectManager */
+$objectManager = Bootstrap::getObjectManager();
+
+/** @var CategoryLinkManagementInterface $categoryLinkManagement */
+$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class);
+
+/** @var $product Product */
+$product = $objectManager->create(Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(Type::TYPE_SIMPLE)
+    ->setId(77)
+    ->setAttributeSetId(4)
+    ->setWebsiteIds([1])
+    ->setName('Simple Product')
+    ->setSku('simple_77')
+    ->setPrice(10)
+    ->setWeight(1)
+    ->setShortDescription("Short description")
+    ->setTaxClassId(0)
+    ->setTierPrice(
+        [
+            [
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL,
+                'price_qty'  => 2,
+                'price'      => 8,
+            ],
+            [
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL,
+                'price_qty'  => 5,
+                'price'      => 5,
+            ],
+            [
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID,
+                'price_qty'  => 3,
+                'price'      => 5,
+            ],
+        ]
+    )
+    ->setDescription('Description with <b>html tag</b>')
+    ->setMetaTitle('meta title')
+    ->setMetaKeyword('meta keyword')
+    ->setMetaDescription('meta description')
+    ->setVisibility(Visibility::VISIBILITY_BOTH)
+    ->setStatus(Status::STATUS_ENABLED)
+    ->setStockData(
+        [
+            'use_config_manage_stock'   => 1,
+            'qty'                       => 100,
+            'is_qty_decimal'            => 0,
+            'is_in_stock'               => 1,
+        ]
+    )->setCanSaveCustomOptions(true)
+    ->setHasOptions(true);
+
+$oldOptions = [
+    [
+        'previous_group' => 'text',
+        'title'     => 'Test Field',
+        'type'      => 'field',
+        'is_require' => 1,
+        'sort_order' => 0,
+        'price'     => 1,
+        'price_type' => 'fixed',
+        'sku'       => '1-text',
+        'max_characters' => 100,
+    ],
+    [
+        'previous_group' => 'date',
+        'title'     => 'Test Date and Time',
+        'type'      => 'date_time',
+        'is_require' => 1,
+        'sort_order' => 0,
+        'price'     => 2,
+        'price_type' => 'fixed',
+        'sku'       => '2-date',
+    ],
+    [
+        'previous_group' => 'select',
+        'title'     => 'Test Select',
+        'type'      => 'drop_down',
+        'is_require' => 1,
+        'sort_order' => 0,
+        'values'    => [
+            [
+                'option_type_id' => -1,
+                'title'         => 'Option 1',
+                'price'         => 3,
+                'price_type'    => 'fixed',
+                'sku'           => '3-1-select',
+            ],
+            [
+                'option_type_id' => -1,
+                'title'         => 'Option 2',
+                'price'         => 3,
+                'price_type'    => 'fixed',
+                'sku'           => '3-2-select',
+            ],
+        ]
+    ],
+    [
+        'previous_group' => 'select',
+        'title'     => 'Test Radio',
+        'type'      => 'radio',
+        'is_require' => 1,
+        'sort_order' => 0,
+        'values'    => [
+            [
+                'option_type_id' => -1,
+                'title'         => 'Option 1',
+                'price'         => 3,
+                'price_type'    => 'fixed',
+                'sku'           => '4-1-radio',
+            ],
+            [
+                'option_type_id' => -1,
+                'title'         => 'Option 2',
+                'price'         => 3,
+                'price_type'    => 'fixed',
+                'sku'           => '4-2-radio',
+            ],
+        ]
+    ]
+];
+
+$options = [];
+
+/** @var ProductCustomOptionInterfaceFactory $customOptionFactory */
+$customOptionFactory = $objectManager->create(ProductCustomOptionInterfaceFactory::class);
+
+foreach ($oldOptions as $option) {
+    /** @var ProductCustomOptionInterface $option */
+    $option = $customOptionFactory->create(['data' => $option]);
+    $option->setProductSku($product->getSku());
+
+    $options[] = $option;
+}
+
+$product->setOptions($options);
+
+/** @var ProductRepositoryInterface $productRepositoryFactory */
+$productRepositoryFactory = $objectManager->create(ProductRepositoryInterface::class);
+$productRepositoryFactory->save($product);
+
+$categoryLinkManagement->assignProductToCategories(
+    $product->getSku(),
+    [2]
+);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
index 00e50477ae4c8df6d9e550d6ec2495a53ec99436..eb6b1f1e3bf941f0c3b718c1edb3889c8b167d63 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
@@ -4,6 +4,8 @@
  * See COPYING.txt for license details.
  */
 
+//use Magento\Catalog\Api\ProductRepositoryInterface;
+
 require 'product_configurable.php';
 /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
 
@@ -18,7 +20,14 @@ $options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
 $option = $options->setAttributeFilter($attribute->getId())->getFirstItem();
 
 $requestInfo = new \Magento\Framework\DataObject(
-    ['qty' => 1, 'super_attribute' => [$attribute->getId() => $option->getId()]]
+    [
+        'product' => 1,
+        'selected_configurable_option' => 1,
+        'qty' => 1,
+        'super_attribute' => [
+            $attribute->getId() => $option->getId()
+        ]
+    ]
 );
 
 /** @var $cart \Magento\Checkout\Model\Cart */
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
index b617f9b8fd2249b88d055c86d0c94c8c90ac302e..47763f83da3d7cad20f233897323820c41aaaebb 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
@@ -6,6 +6,10 @@
 namespace Magento\Quote\Model;
 
 use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\Api\FilterBuilder;
+use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Quote\Api\Data\CartInterface;
 
 class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
 {
@@ -13,6 +17,55 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
      * @magentoDataFixture Magento/Sales/_files/quote.php
      */
     public function testGetList()
+    {
+        $searchCriteria = $this->getSearchCriteria('test01');
+            /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */
+        $quoteRepository = Bootstrap::getObjectManager()->create(CartRepositoryInterface::class);
+        $searchResult = $quoteRepository->getList($searchCriteria);
+        $this->performAssertions($searchResult);
+    }
+
+    /**
+     * @magentoDataFixture Magento/Sales/_files/quote.php
+     */
+    public function testGetListDoubleCall()
+    {
+        $searchCriteria1 = $this->getSearchCriteria('test01');
+        $searchCriteria2 = $this->getSearchCriteria('test02');
+
+        /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */
+        $quoteRepository = Bootstrap::getObjectManager()->create(CartRepositoryInterface::class);
+        $searchResult = $quoteRepository->getList($searchCriteria1);
+        $this->performAssertions($searchResult);
+        $searchResult = $quoteRepository->getList($searchCriteria2);
+        $items = $searchResult->getItems();
+        $this->assertEmpty($items);
+    }
+
+    /**
+     * @param string $filterValue
+     * @return \Magento\Framework\Api\SearchCriteria
+     */
+    private function getSearchCriteria($filterValue)
+    {
+        /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder */
+        $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class);
+        $filterBuilder = Bootstrap::getObjectManager()->create(FilterBuilder::class);
+        $filters = [];
+        $filters[] = $filterBuilder
+            ->setField('reserved_order_id')
+            ->setConditionType('=')
+            ->setValue($filterValue)
+            ->create();
+        $searchCriteriaBuilder->addFilters($filters);
+
+        return $searchCriteriaBuilder->create();
+    }
+
+    /**
+     * @param object $searchResult
+     */
+    protected function performAssertions($searchResult)
     {
         $expectedExtensionAttributes = [
             'firstname' => 'firstname',
@@ -20,16 +73,10 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
             'email' => 'admin@example.com'
         ];
 
-        /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder */
-        $searchCriteriaBuilder = Bootstrap::getObjectManager()->create('Magento\Framework\Api\SearchCriteriaBuilder');
-
-        /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */
-        $quoteRepository = Bootstrap::getObjectManager()->create('Magento\Quote\Api\CartRepositoryInterface');
-        $searchResult = $quoteRepository->getList($searchCriteriaBuilder->create());
         $items = $searchResult->getItems();
         /** @var \Magento\Quote\Api\Data\CartInterface $actualQuote */
         $actualQuote = array_pop($items);
-        $this->assertInstanceOf('Magento\Quote\Api\Data\CartInterface', $actualQuote);
+        $this->assertInstanceOf(CartInterface::class, $actualQuote);
         $this->assertEquals('test01', $actualQuote->getReservedOrderId());
         /** @var \Magento\User\Api\Data\UserInterface $testAttribute */
         $testAttribute = $actualQuote->getExtensionAttributes()->getQuoteTestAttribute();
diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7b41e4f9d5d52cd261807cc46e5de55e005f7eed
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php
@@ -0,0 +1,165 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Search\Model\SearchEngine;
+
+/**
+ * Class ConfigTest
+ *
+ * @magentoAppIsolation enabled
+ */
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        $xmlPath = __DIR__ . '/../../_files/search_engine.xml';
+        $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+        // Clear out the clache
+        $cacheManager = $objectManager->create('Magento\Framework\App\Cache\Manager');
+        /** @var \Magento\Framework\App\Cache\Manager $cacheManager */
+        $cacheManager->clean($cacheManager->getAvailableTypes());
+
+        $fileResolver = $this->getMockForAbstractClass(
+            'Magento\Framework\Config\FileResolverInterface',
+            [],
+            '',
+            false
+        );
+        $fileResolver->expects($this->any())->method('get')->willReturn([file_get_contents($xmlPath)]);
+
+        $configReader = $objectManager->create(
+            'Magento\Framework\Search\SearchEngine\Config\Reader',
+            ['fileResolver' => $fileResolver]
+        );
+        $dataStorage = $objectManager->create(
+            'Magento\Search\Model\SearchEngine\Config\Data',
+            ['reader' => $configReader]
+        );
+        $this->config = $objectManager->create(
+            'Magento\Search\Model\SearchEngine\Config',
+            ['dataStorage' => $dataStorage]
+        );
+    }
+
+    /**
+     * Data provider for the test
+     *
+     * @return array
+     */
+    public static function loadGetDeclaredFeaturesDataProvider()
+    {
+        return [
+            'features-synonyms' => [
+                'searchEngine' => 'mysql',
+                'expectedResult' => ['synonyms']
+            ],
+            'features-synonyms-stopwords' => [
+                'searchEngine' => 'other',
+                'expectedResult' => ['synonyms', 'stopwords']
+            ],
+            'features-none1' => [
+                'searchEngine' => 'none1',
+                'expectedResult' => []
+            ],
+            'features-none2' => [
+                'searchEngine' => 'none2',
+                'expectedResult' => []
+            ],
+            'features-none_exist' => [
+                'searchEngine' => 'none_exist',
+                'expectedResult' => []
+            ]
+
+        ];
+    }
+
+    /**
+     * @param string $searchEngine
+     * @param array $expectedResult
+     * @dataProvider loadGetDeclaredFeaturesDataProvider
+     */
+    public function testGetDeclaredFeatures($searchEngine, $expectedResult)
+    {
+        $this->assertEquals($expectedResult, $this->config->getDeclaredFeatures($searchEngine));
+    }
+
+    /**
+     * Data provider for the test
+     *
+     * @return array
+     */
+    public static function loadIsFeatureSupportedDataProvider()
+    {
+        return [
+            [
+                'feature' => 'synonyms',
+                'searchEngine' => 'mysql',
+                'expectedResult' => true
+            ],
+            [
+                'feature' => 'stopwords',
+                'searchEngine' => 'mysql',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'synonyms',
+                'searchEngine' => 'other',
+                'expectedResult' => true
+            ],
+            [
+                'feature' => 'stopwords',
+                'searchEngine' => 'other',
+                'expectedResult' => true
+            ],
+            [
+                'feature' => 'synonyms',
+                'searchEngine' => 'none1',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'stopwords',
+                'searchEngine' => 'none1',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'synonyms',
+                'searchEngine' => 'none2',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'stopwords',
+                'searchEngine' => 'none2',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'stopwords',
+                'searchEngine' => 'none_exist',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'none_exist',
+                'searchEngine' => 'none_exist',
+                'expectedResult' => false
+            ],
+            [
+                'feature' => 'none_exist',
+                'searchEngine' => 'mysql',
+                'expectedResult' => false
+            ]
+        ];
+    }
+
+    /**
+     * @param string $searchEngine
+     * @param string $feature
+     * @param array $expectedResult
+     * @dataProvider loadIsFeatureSupportedDataProvider
+     */
+    public function testIsFeatureSupported($searchEngine, $feature, $expectedResult)
+    {
+        $this->assertEquals($expectedResult, $this->config->isFeatureSupported($searchEngine, $feature));
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php
index 99aa10d6e7cd029b5db3a093da53d6fbf5fd535d..9980f94a92ab564f1c806f57e79199788be7139c 100644
--- a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php
@@ -31,64 +31,25 @@ class SynonymAnalyzerTest extends \PHPUnit_Framework_TestCase
     {
         return [
             'WithSynonymsFromStoreViewScope' => [
-                'phrase' => 'Elizabeth is the English queen.',
+                'phrase' => 'elizabeth is the english queen',
                 'expectedResult' => [['elizabeth'],['is'],['the'],['british', 'english'],['queen', 'monarch']]
             ],
             'WithSynonymsFromWebsiteScope' => [
-                'phrase' => 'Orange hill',
+                'phrase' => 'orange hill',
                 'expectedResult' => [['orange', 'magento'], ['hill', 'mountain', 'peak']]
             ],
             'WithSynonymsFromDefaultScope' => [
-                'phrase' => 'universe is enormous.',
+                'phrase' => 'universe is enormous',
                 'expectedResult' => [['universe', 'cosmos'], ['is'], ['big', 'huge', 'large', 'enormous']]
             ],
             'noSynonyms' => [
-                'phrase' => 'This sentence has no synonyms',
+                'phrase' => 'this sentence has no synonyms',
                 'expectedResult' => [['this'], ['sentence'], ['has'], ['no'], ['synonyms']]
             ],
-            'specialCharacters' => [
-                'phrase' => '~tilde`backtic! exclamation@  at#hash\$dollar%percent^carat&ampersand*star(leftparan'
-                    . ')rightparan_underscore+plus=equal{leftcurly}rightcurly[leftbracket]rightbracket:colon'
-                    . '"doublequote\'singlequote,comma  space.period<leftangle>rightangle?questionmark\\backslash'
-                    . '/forwardslash   tab;semicolon',
-                'expectedResult' => [
-                    ['tilde'],
-                    ['backtic'],
-                    ['exclamation'],
-                    ['at'],
-                    ['hash'],
-                    ['dollar'],
-                    ['percent'],
-                    ['carat'],
-                    ['ampersand'],
-                    ['star'],
-                    ['leftparan'],
-                    ['rightparan'],
-                    ['underscore'],
-                    ['plus'],
-                    ['equal'],
-                    ['leftcurly'],
-                    ['rightcurly'],
-                    ['leftbracket'],
-                    ['rightbracket'],
-                    ['colon'],
-                    ['doublequote'],
-                    ['singlequote'],
-                    ['comma'],
-                    ['space'],
-                    ['period'],
-                    ['leftangle'],
-                    ['rightangle'],
-                    ['questionmark'],
-                    ['backslash'],
-                    ['forwardslash'],
-                    ['tab'],
-                    ['semicolon']
-                ]
-            ],
             'oneMoreTest' => [
                 'phrase' => 'schlicht',
-                'expectedResult' => [['schlicht', 'natürlich']]]
+                'expectedResult' => [['schlicht', 'natürlich']]
+            ],
         ];
     }
 
diff --git a/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml b/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dcee4dabaa8dacabfc44f03b636f3d92e4093d39
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
+    <engine name="mysql">
+        <feature name="synonyms" support="1" />
+        <feature name="stopwords" support="0" />
+    </engine>
+    <engine name="other">
+        <feature name="synonyms" support="1" />
+        <feature name="stopwords" support="1" />
+    </engine>
+    <engine name="none1">
+        <feature name="synonyms" support="0" />
+        <feature name="stopwords" support="0" />
+    </engine>
+    <engine name="none2" />
+</engines>
diff --git a/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php b/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php
index 3bfc2aa21ef65ec1907bbccaeb64b9e3270af263..135c87b1ad88fa3aa03a129885d4938e772d9062 100644
--- a/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php
+++ b/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php
@@ -8,18 +8,58 @@ namespace Magento\Translation\Controller;
 
 class AjaxTest extends \Magento\TestFramework\TestCase\AbstractController
 {
+    protected function setUp()
+    {
+        /* Called getConfig as workaround for setConfig bug */
+        \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+            'Magento\Store\Model\StoreManagerInterface'
+        )->getStore(
+            'default'
+        )->getConfig(
+            'dev/translate_inline/active'
+        );
+        \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+            'Magento\Framework\App\Config\MutableScopeConfigInterface'
+        )->setValue(
+            'dev/translate_inline/active',
+            true,
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+            'default'
+        );
+        parent::setUp();
+    }
+
     /**
      * @dataProvider indexActionDataProvider
      */
-    public function testIndexAction($postData)
+    public function testIndexAction($postData, $expected)
     {
         $this->getRequest()->setPostValue('translate', $postData);
         $this->dispatch('translation/ajax/index');
-        $this->assertEquals('{success:true}', $this->getResponse()->getBody());
+        $this->assertEquals($expected, $this->getResponse()->getBody());
     }
 
     public function indexActionDataProvider()
     {
-        return [['test'], [['test']]];
+        return [
+            [
+                [
+                    [
+                        'original' => 'phrase1',
+                        'custom' => 'translation1'
+                    ]
+                ],
+                '{"phrase1":"translation1"}'
+            ],
+            [
+                [
+                    [
+                        'original' => 'phrase2',
+                        'custom' => 'translation2'
+                    ]
+                ],
+                '{"phrase1":"translation1","phrase2":"translation2"}'
+            ]
+        ];
     }
 }
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php
similarity index 90%
rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php
rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php
index 21d35b9e428e20fca7fd86c4a0d2bab68deaa721..d8eef79532b57daace020258c2fab480c58e61aa 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php
@@ -7,7 +7,7 @@
  */
 namespace Magento\Test\Integrity\Magento\Framework\Search;
 
-class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
+class RequestConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
 {
     /** @var \Magento\Framework\Config\Dom\UrnResolver */
     protected $urnResolver;
@@ -44,7 +44,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
      */
     protected function _getKnownValidXml()
     {
-        return __DIR__ . '/_files/valid.xml';
+        return __DIR__ . '/_files/request/valid.xml';
     }
 
     /**
@@ -54,7 +54,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
      */
     protected function _getKnownInvalidXml()
     {
-        return __DIR__ . '/_files/invalid.xml';
+        return __DIR__ . '/_files/request/invalid.xml';
     }
 
     /**
@@ -64,7 +64,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
      */
     protected function _getKnownValidPartialXml()
     {
-        return __DIR__ . '/_files/valid_partial.xml';
+        return __DIR__ . '/_files/request/valid_partial.xml';
     }
 
     /**
@@ -76,7 +76,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
             explode(
                 "\n",
                 "
-No match found for key-sequence ['sugegsted_search_container'] of keyref 'requestQueryReference'.
+No match found for key-sequence ['suggested_search_container'] of keyref 'requestQueryReference'.
 Element 'queryReference': No match found for key-sequence ['fulltext_search_query4'] of keyref 'queryReference'.
 "
             )
@@ -126,7 +126,7 @@ Element 'request': Missing child element(s). Expected is ( from )."
      */
     protected function _getKnownInvalidPartialXml()
     {
-        return __DIR__ . '/_files/invalid_partial.xml';
+        return __DIR__ . '/_files/request/invalid_partial.xml';
     }
 
     public function testSchemaUsingValidXml()
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f17525512d9490edbf8b48842ec5b2d7cb144be5
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Test search_request.xsd and xml files.
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Test\Integrity\Magento\Framework\Search;
+
+class SearchEngineConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig
+{
+    /** @var \Magento\Framework\Config\Dom\UrnResolver */
+    protected $urnResolver;
+
+    protected function setUp()
+    {
+        $this->urnResolver = new \Magento\Framework\Config\Dom\UrnResolver();
+    }
+
+    /**
+     * Returns the name of the XSD file to be used to validate the XML
+     *
+     * @return string
+     */
+    protected function _getXsd()
+    {
+        return $this->urnResolver->getRealPath('urn:magento:framework:Search/etc/search_engine.xsd');
+    }
+
+    /**
+     * Returns the name of the XSD file to be used to validate partial XML
+     *
+     * @return string
+     */
+    protected function _getFileXsd()
+    {
+        return $this->urnResolver->getRealPath('urn:magento:framework:Search/etc/search_engine.xsd');
+    }
+
+    /**
+     * The location of a single valid complete xml file
+     *
+     * @return string
+     */
+    protected function _getKnownValidXml()
+    {
+        return __DIR__ . '/_files/search_engine/valid.xml';
+    }
+
+    /**
+     * The location of a single known invalid complete xml file
+     *
+     * @return string
+     */
+    protected function _getKnownInvalidXml()
+    {
+        return __DIR__ . '/_files/search_engine/invalid.xml';
+    }
+
+    /**
+     * The location of a single known valid partial xml file
+     *
+     * @return string
+     */
+    protected function _getKnownValidPartialXml()
+    {
+        return null;
+    }
+
+    /**
+     * @param null $expectedErrors
+     */
+    public function testSchemaUsingInvalidXml($expectedErrors = null)
+    {
+        $expectedErrors = array_filter(
+            explode(
+                "\n",
+                "
+Element 'feature': The attribute 'support' is required but missing.
+Element 'wrong': This element is not expected. Expected is ( feature ).
+Element 'feature': The attribute 'name' is required but missing.
+Element 'engine', attribute 'wrong': The attribute 'wrong' is not allowed.
+Element 'engine': The attribute 'name' is required but missing.
+Element 'feature', attribute 'support': 'wrong' is not a valid value of the atomic type 'xs:boolean'.
+"
+            )
+        );
+        parent::testSchemaUsingInvalidXml($expectedErrors);
+    }
+
+    /**
+     * Returns the name of the xml files to validate
+     *
+     * @return string
+     */
+    protected function _getXmlName()
+    {
+        return 'search_engine.xml';
+    }
+
+    /**
+     * The location of a single known invalid partial xml file
+     *
+     * @return string
+     */
+    protected function _getKnownInvalidPartialXml()
+    {
+        return null;
+    }
+
+    public function testSchemaUsingValidXml()
+    {
+        parent::testSchemaUsingValidXml();
+    }
+}
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml
similarity index 90%
rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml
rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml
index 9abe310f942b64b9633996dde5ad1cdff130e51e..70ddfa820b9657dc9a24801c60c2c1ab4563b820 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml
@@ -6,7 +6,7 @@
  */
 -->
 <requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd">
-    <request query="sugegsted_search_container" index="product">
+    <request query="suggested_search_container" index="product">
         <queries>
             <query xsi:type="boolQuery" name="suggested_search_container_dd" boost="2">
                 <queryReference clause="not" ref="fulltext_search_query4" />
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid_partial.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid_partial.xml
similarity index 100%
rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid_partial.xml
rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid_partial.xml
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid.xml
similarity index 100%
rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid.xml
rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid.xml
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid_partial.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid_partial.xml
similarity index 100%
rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid_partial.xml
rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid_partial.xml
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml
new file mode 100644
index 0000000000000000000000000000000000000000..575d29fd23a7f354deeb96a50261a974d3b929c5
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
+    <engine name="engine1">
+        <feature name="feature1"/>
+        <wrong name="feature2" support="1" />
+    </engine>
+    <engine name="engine2">
+        <feature support="1" />
+    </engine>
+    <engine wrong="engine3">
+        <feature name="feature1" support="wrong" />
+        <feature name="feature2" support="0" />
+    </engine>
+</engines>
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dca8671e217c9d44ef2fc89adfb482e0ef8f9b99
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
+    <engine name="engine1">
+        <feature name="feature1" support="true" />
+        <feature name="feature2" support="1" />
+    </engine>
+    <engine name="engine2">
+        <feature name="feature1" support="1" />
+        <feature name="feature2" support="false" />
+    </engine>
+    <engine name="engine3">
+        <feature name="feature1" support="0" />
+        <feature name="feature2" support="0" />
+    </engine>
+</engines>
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php
index ff6091a8e1f743efbdeee30b269942fb56146d23..578f58b606dd342ea94ffc2969eb323358d92bc1 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php
@@ -2518,4 +2518,5 @@ return [
     ['fillFormTab', 'Magento\Backend\Test\Block\Widget\Tab', 'Magento\Ui\Test\Block\Adminhtml\AbstractContainer::setFieldsData'],
     ['getDataFormTab', 'Magento\Backend\Test\Block\Widget\Tab', 'Magento\Ui\Test\Block\Adminhtml\AbstractContainer::getFieldsData'],
     ['getBunchImages', 'Magento\CatalogImportExport\Model\Import\Product'],
+    ['_isAttributeValueEmpty', 'Magento\Catalog\Model\ResourceModel\AbstractResource'],
 ];
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index d5a5aa20d2961d9c28415f3a36eeb5b764ce20db..db7f389c81253e6e4cf35f7b328a4116dd4ad0a1 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -2789,6 +2789,9 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
                 if (($key == 'seq') || ($key == 'sneq')) {
                     $key = $this->_transformStringSqlCondition($key, $value);
                 }
+                if (($key == 'in' || $key == 'nin') && is_string($value)) {
+                    $value = explode(',', $value);
+                }
                 $query = $this->_prepareQuotedSqlCondition($conditionKeyMap[$key], $value, $fieldName);
             } else {
                 $queries = [];
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
index f3d029454c21da9460a5a2869d4169d7382cdc22..52bf37f794b235af488fcf3a28649da75c2d940f 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
@@ -347,37 +347,22 @@ abstract class AbstractElement extends AbstractForm
         $html = '';
         $htmlId = $this->getHtmlId();
 
-        if (($beforeElementHtml = $this->getBeforeElementHtml())) {
-            $html .= '<label class="addbefore" for="' .
-                $htmlId .
-                '">' .
-                $beforeElementHtml .
-                '</label>';
+        $beforeElementHtml = $this->getBeforeElementHtml();
+        if ($beforeElementHtml) {
+            $html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>';
         }
 
-        $html .= '<input id="' .
-            $htmlId .
-            '" name="' .
-            $this->getName() .
-            '" ' .
-            $this->_getUiId() .
-            ' value="' .
-            $this->getEscapedValue() .
-            '" ' .
-            $this->serialize(
-                $this->getHtmlAttributes()
-            ) . '/>';
-
-        if (($afterElementJs = $this->getAfterElementJs())) {
+        $html .= '<input id="' . $htmlId . '" name="' . $this->getName() . '" ' . $this->_getUiId() . ' value="' .
+            $this->getEscapedValue() . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>';
+
+        $afterElementJs = $this->getAfterElementJs();
+        if ($afterElementJs) {
             $html .= $afterElementJs;
         }
 
-        if (($afterElementHtml = $this->getAfterElementHtml())) {
-            $html .= '<label class="addafter" for="' .
-                $htmlId .
-                '">' .
-                $afterElementHtml .
-                '</label>';
+        $afterElementHtml = $this->getAfterElementHtml();
+        if ($afterElementHtml) {
+            $html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>';
         }
 
         return $html;
diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ba97920fd8e47dea4231154400d07c929dd2a40
--- /dev/null
+++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Filesystem\Test\Unit\Driver;
+
+use Magento\Framework\Filesystem\Driver\File;
+
+class FileTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var string Result of file_get_contents() function */
+    public static $fileGetContents;
+
+    /** @var bool Result of file_put_contents() function */
+    public static $filePutContents;
+
+    public function setUp()
+    {
+        self::$fileGetContents = '';
+        self::$filePutContents = true;
+    }
+
+    /**
+     * @dataProvider dataProviderForTestGetAbsolutePath
+     */
+    public function testGetAbsolutePath($basePath, $path, $expected)
+    {
+        $file = new File();
+        $this->assertEquals($expected, $file->getAbsolutePath($basePath, $path));
+    }
+    
+    public function dataProviderForTestGetAbsolutePath()
+    {
+        return [
+            ['/root/path/', 'sub', '/root/path/sub'],
+            ['/root/path/', '/sub', '/root/path/sub'],
+            ['/root/path/', '../sub', '/root/path/../sub'],
+            ['/root/path/', '/root/path/sub', '/root/path/root/path/sub'],
+        ];
+    }
+
+    /**
+     * @dataProvider dataProviderForTestGetRelativePath
+     */
+    public function testGetRelativePath($basePath, $path, $expected)
+    {
+        $file = new File();
+        $this->assertEquals($expected, $file->getRelativePath($basePath, $path));
+    }
+
+    public function dataProviderForTestGetRelativePath()
+    {
+        return [
+            ['/root/path/', 'sub', 'sub'],
+            ['/root/path/', '/sub', '/sub'],
+            ['/root/path/', '/root/path/sub', 'sub'],
+            ['/root/path/sub', '/root/path/other', '/root/path/other'],
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
index 1d3a7bd7e093e677d61daf66471ca58737f716dc..8d812795fff1b911664f73a9f433adb74a9b0068 100644
--- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
+++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
@@ -44,6 +44,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
         $this->_fileMimeType = null;
         $this->_fileType = null;
     }
+
     /**
      * Open image for processing
      *
@@ -60,6 +61,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
         if ($this->_isMemoryLimitReached()) {
             throw new \OverflowException('Memory limit has been reached.');
         }
+        $this->imageDestroy();
         $this->_imageHandler = call_user_func($this->_getCallback('create'), $this->_fileName);
     }
 
@@ -151,6 +153,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
                 }
                 $this->_fillBackgroundColor($newImage);
                 imagecopy($newImage, $this->_imageHandler, 0, 0, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight);
+                $this->imageDestroy();
                 $this->_imageHandler = $newImage;
             }
         }
@@ -361,6 +364,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
                 $this->_imageSrcHeight
             );
         }
+        $this->imageDestroy();
         $this->_imageHandler = $newImage;
         $this->refreshImageDimensions();
         $this->_resized = true;
@@ -374,7 +378,9 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
      */
     public function rotate($angle)
     {
-        $this->_imageHandler = imagerotate($this->_imageHandler, $angle, $this->imageBackgroundColor);
+        $rotatedImage = imagerotate($this->_imageHandler, $angle, $this->imageBackgroundColor);
+        $this->imageDestroy();
+        $this->_imageHandler = $rotatedImage;
         $this->refreshImageDimensions();
     }
 
@@ -595,7 +601,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
             $newWidth,
             $newHeight
         );
-
+        $this->imageDestroy();
         $this->_imageHandler = $canvas;
         $this->refreshImageDimensions();
         return true;
@@ -631,6 +637,16 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
      * Standard destructor. Destroy stored information about image
      */
     public function __destruct()
+    {
+        $this->imageDestroy();
+    }
+
+    /**
+     * Helper function to free up memory associated with _imageHandler resource
+     *
+     * @return void
+     */
+    private function imageDestroy()
     {
         if (is_resource($this->_imageHandler)) {
             imagedestroy($this->_imageHandler);
@@ -755,6 +771,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
         imagesavealpha($image, true);
 
         imagefill($image, 0, 0, $colorWhite);
+        $this->imageDestroy();
         $this->_imageHandler = $image;
     }
 }
diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php
index a727144c93a6855e35f2d1ef083507371dc6ca95..2232ece0b93d6985b78326c7e9dc9db30435016f 100644
--- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php
+++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php
@@ -12,6 +12,7 @@ use Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface;
 use Magento\Framework\Search\Adapter\Mysql\ScoreBuilder;
 use Magento\Framework\Search\Request\Query\BoolExpression;
 use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
+use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface;
 
 class Match implements QueryInterface
 {
@@ -39,20 +40,28 @@ class Match implements QueryInterface
      */
     private $fulltextSearchMode;
 
+    /**
+     * @var PreprocessorInterface[]
+     */
+    protected $preprocessors;
+
     /**
      * @param ResolverInterface $resolver
      * @param Fulltext $fulltextHelper
      * @param string $fulltextSearchMode
+     * @param PreprocessorInterface[] $preprocessors
      */
     public function __construct(
         ResolverInterface $resolver,
         Fulltext $fulltextHelper,
-        $fulltextSearchMode = Fulltext::FULLTEXT_MODE_BOOLEAN
+        $fulltextSearchMode = Fulltext::FULLTEXT_MODE_BOOLEAN,
+        array $preprocessors = []
     ) {
         $this->resolver = $resolver;
         $this->replaceSymbols = str_split(self::SPECIAL_CHARACTERS, 1);
         $this->fulltextHelper = $fulltextHelper;
         $this->fulltextSearchMode = $fulltextSearchMode;
+        $this->preprocessors = $preprocessors;
     }
 
     /**
@@ -107,6 +116,9 @@ class Match implements QueryInterface
     protected function prepareQuery($queryValue, $conditionType)
     {
         $queryValue = str_replace($this->replaceSymbols, ' ', $queryValue);
+        foreach ($this->preprocessors as $preprocessor) {
+            $queryValue = $preprocessor->process($queryValue);
+        }
 
         $stringPrefix = '';
         if ($conditionType === BoolExpression::QUERY_CONDITION_MUST) {
diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
index a01690cf580d03ec7824301412913a75cfc161ed..8a9e28c7d13a8f9cf8559d041b6698fde4ff3a5c 100644
--- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
+++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
@@ -31,6 +31,8 @@ class TemporaryStorage
     }
 
     /**
+     * Stores Documents
+     *
      * @param \ArrayIterator|\Magento\Framework\Search\Document[] $documents
      * @return Table
      */
@@ -44,7 +46,38 @@ class TemporaryStorage
             ];
         }
 
-        $table = $this->createTemporaryTable();
+        return $this->populateTemporaryTable($this->createTemporaryTable(), $data);
+    }
+
+    /**
+     * Stores Api type Documents
+     *
+     * @param \Magento\Framework\Api\Search\DocumentInterface[] $documents
+     * @return Table
+     */
+    public function storeApiDocuments($documents)
+    {
+        $data = [];
+        foreach ($documents as $document) {
+            $data[] = [
+                $document->getId(),
+                $document->getCustomAttribute('score')->getValue(),
+            ];
+        }
+
+        return $this->populateTemporaryTable($this->createTemporaryTable(), $data);
+    }
+
+    /**
+     * Populates temporary table
+     *
+     * @param Table $table
+     * @param array $data
+     * @return Table
+     * @throws \Zend_Db_Exception
+     */
+    private function populateTemporaryTable(Table $table, $data)
+    {
         if (count($data)) {
             $this->getConnection()->insertArray(
                 $table->getName(),
diff --git a/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php b/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..322f68610d3c2cbdfbefb916427c973b36742bcc
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\Adapter\Preprocessor;
+
+interface PreprocessorInterface
+{
+    /**
+     * @param string $query
+     * @return string
+     */
+    public function process($query);
+}
diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php
new file mode 100644
index 0000000000000000000000000000000000000000..897b6a999a8952c06dbb125828fca785966b2973
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\SearchEngine\Config;
+
+use Magento\Framework\Config\ConverterInterface;
+
+class Converter implements ConverterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function convert($source)
+    {
+        $result = [];
+        /** @var \DOMElement $engine */
+        foreach ($source->documentElement->getElementsByTagName('engine') as $engine) {
+            $name = $engine->getAttribute('name');
+            $result[$name] = [];
+            /** @var \DOMElement $feature */
+            foreach ($engine->getElementsByTagName('feature') as $feature) {
+                if ($feature->getAttribute('support') === '1'
+                    || strtolower($feature->getAttribute('support')) === 'true'
+                ) {
+                    $result[$name][] = strtolower($feature->getAttribute('name'));
+                }
+            }
+        }
+        return $result;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php
new file mode 100644
index 0000000000000000000000000000000000000000..dbf9c610d8f349a5d771fe799691b9c24add5a76
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\SearchEngine\Config;
+
+class Reader extends \Magento\Framework\Config\Reader\Filesystem
+{
+    /**
+     * List of id attributes for merge
+     *
+     * @var array
+     */
+    protected $_idAttributes = ['/engines/engine' => 'name'];
+
+    /**
+     * Constructor
+     *
+     * @param \Magento\Framework\Config\FileResolverInterface $fileResolver
+     * @param Converter $converter
+     * @param SchemaLocator $schemaLocator
+     * @param \Magento\Framework\Config\ValidationStateInterface $validationState
+     * @param string $fileName
+     * @param array $idAttributes
+     * @param string $domDocumentClass
+     * @param string $defaultScope
+     */
+    public function __construct(
+        \Magento\Framework\Config\FileResolverInterface $fileResolver,
+        Converter $converter,
+        SchemaLocator $schemaLocator,
+        \Magento\Framework\Config\ValidationStateInterface $validationState,
+        $fileName = 'search_engine.xml',
+        $idAttributes = [],
+        $domDocumentClass = 'Magento\Framework\Config\Dom',
+        $defaultScope = 'global'
+    ) {
+        parent::__construct(
+            $fileResolver,
+            $converter,
+            $schemaLocator,
+            $validationState,
+            $fileName,
+            $idAttributes,
+            $domDocumentClass,
+            $defaultScope
+        );
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..c822cf026cd7ccdddea4a0b1012a9fd38e2d4ad8
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\SearchEngine\Config;
+
+use Magento\Framework\Config\SchemaLocatorInterface;
+
+class SchemaLocator implements SchemaLocatorInterface
+{
+    const SEARCH_ENGINE_XSD_PATH = 'urn:magento:framework:Search/etc/search_engine.xsd';
+
+    /**
+     * URN resolver
+     *
+     * @var \Magento\Framework\Config\Dom\UrnResolver
+     */
+    protected $urnResolver;
+
+    /**
+     * Constructor
+     *
+     * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver
+     */
+    public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolver)
+    {
+        $this->urnResolver = $urnResolver;
+    }
+
+    /**
+     * Get path to merged config schema
+     *
+     * @return string|null
+     */
+    public function getSchema()
+    {
+        return $this->urnResolver->getRealPath(self::SEARCH_ENGINE_XSD_PATH);
+    }
+
+    /**
+     * Get path to per file validation schema
+     *
+     * @return string|null
+     */
+    public function getPerFileSchema()
+    {
+        return $this->urnResolver->getRealPath(self::SEARCH_ENGINE_XSD_PATH);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php b/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..44233316368ba79166448f38a81d105ad2cfe50a
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\SearchEngine;
+
+interface ConfigInterface
+{
+    /**
+     * Search engine feature: synonyms
+     */
+    const SEARCH_ENGINE_FEATURE_SYNONYMS = 'synonyms';
+
+    /**
+     * Get declared features of a search engine
+     *
+     * @param string $searchEngine
+     * @return string[]
+     */
+    public function getDeclaredFeatures($searchEngine);
+
+    /**
+     * Checks if a particular search feature is supported
+     *
+     * @param string $featureName
+     * @param string $searchEngine
+     * @return bool
+     */
+    public function isFeatureSupported($featureName, $searchEngine);
+}
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php
index 901474b5773c5712815c477ec483361adc5da716..528b2f9890138ac5b00538903d7d82eb933fe229 100644
--- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php
@@ -8,16 +8,17 @@ namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql\Query\Builder;
 use Magento\Framework\DB\Select;
 use Magento\Framework\Search\Request\Query\BoolExpression;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
 class MatchTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Framework\Search\Adapter\Mysql\ScoreBuilder|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\Search\Adapter\Mysql\ScoreBuilder|MockObject
      */
     private $scoreBuilder;
 
     /**
-     * @var \Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface|MockObject
      */
     private $resolver;
 
@@ -27,10 +28,15 @@ class MatchTest extends \PHPUnit_Framework_TestCase
     private $match;
 
     /**
-     * @var \Magento\Framework\DB\Helper\Mysql\Fulltext|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\DB\Helper\Mysql\Fulltext|MockObject
      */
     private $fulltextHelper;
 
+    /**
+     * @var \Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface|MockObject
+     */
+    private $preprocessor;
+
     protected function setUp()
     {
         $helper = new ObjectManager($this);
@@ -49,19 +55,32 @@ class MatchTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
+        $this->preprocessor = $this->getMockBuilder('Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms')
+            ->setMethods(['process'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $this->match = $helper->getObject(
             'Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match',
-            ['resolver' => $this->resolver, 'fulltextHelper' => $this->fulltextHelper]
+            [
+                'resolver' => $this->resolver,
+                'fulltextHelper' => $this->fulltextHelper,
+                'preprocessors' => [$this->preprocessor]
+            ]
         );
     }
 
-    public function testBuildQuery()
+    public function testBuild()
     {
         /** @var Select|\PHPUnit_Framework_MockObject_MockObject $select */
         $select = $this->getMockBuilder('Magento\Framework\DB\Select')
             ->setMethods(['getMatchQuery', 'match', 'where'])
             ->disableOriginalConstructor()
             ->getMock();
+        $this->preprocessor->expects($this->once())
+            ->method('process')
+            ->with($this->equalTo('some_value '))
+            ->will($this->returnValue('some_value '));
         $this->fulltextHelper->expects($this->once())
             ->method('getMatchQuery')
             ->with($this->equalTo(['some_field' => 'some_field']), $this->equalTo('-some_value*'))
@@ -102,7 +121,13 @@ class MatchTest extends \PHPUnit_Framework_TestCase
         $this->scoreBuilder->expects($this->once())
             ->method('addCondition');
 
-        $result = $this->match->build($this->scoreBuilder, $select, $query, BoolExpression::QUERY_CONDITION_NOT);
+        $result = $this->match->build(
+            $this->scoreBuilder,
+            $select,
+            $query,
+            BoolExpression::QUERY_CONDITION_NOT,
+            [$this->preprocessor]
+        );
 
         $this->assertEquals($select, $result);
     }
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php
index cdbcd2e2c47fbcf3ccfa38fd3bb93b49bd03d21c..4a3421a5ffaab995010cde5bfed98dfec76a8401 100644
--- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php
@@ -108,6 +108,36 @@ class TemporaryStorageTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($result, $table);
     }
 
+    public function testStoreApiDocuments()
+    {
+        $documentId = 312432;
+        $documentValue = 1.235123;
+
+        $attributeValue = $this->getMockBuilder('Magento\Framework\Api\AttributeValue')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $attributeValue->expects($this->once())
+            ->method('getValue')
+            ->willReturn($documentValue);
+
+        $document = $this->getMockBuilder('Magento\Framework\Api\Search\Document')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $document->expects($this->once())
+            ->method('getId')
+            ->willReturn($documentId);
+        $document->expects($this->once())
+            ->method('getCustomAttribute')
+            ->with('score')
+            ->willReturn($attributeValue);
+
+        $table = $this->createTemporaryTable();
+
+        $result = $this->model->storeApiDocuments([$document]);
+
+        $this->assertEquals($result, $table);
+    }
+
     /**
      * @return \Magento\Framework\DB\Ddl\Table|\PHPUnit_Framework_MockObject_MockObject
      */
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..9713beef5fb1efaea0a5194ee3b87ba314aa33e0
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\Test\Unit\SearchEngine\Config;
+
+use Magento\Framework\Search\SearchEngine\Config\Converter;
+
+class ConverterTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConvert()
+    {
+        $converter = new Converter();
+        $dom = new \DOMDocument();
+        $dom->load(realpath(__DIR__ . '/../../_files/search_engine.xml'));
+        $result = $converter->convert($dom);
+        $expected = [
+            'mysql' => ['synonyms'],
+            'other' => ['synonyms', 'stopwords'],
+            'none' => [],
+        ];
+        $this->assertEquals($expected, $result);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..803e6ac78922bf3c89869cba653e01a9ec364a6c
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Search\Test\Unit\SearchEngine\Config;
+
+use Magento\Framework\Search\SearchEngine\Config\SchemaLocator;
+
+class SchemaLocatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var SchemaLocator
+     */
+    private $model;
+
+    protected function setUp()
+    {
+        $urnResolver = $this->getMock('Magento\Framework\Config\Dom\UrnResolver', [], [], '', false);
+        $urnResolver->expects($this->any())
+            ->method('getRealPath')
+            ->with(SchemaLocator::SEARCH_ENGINE_XSD_PATH)
+            ->willReturn('xsd/path');
+
+        $this->model = new SchemaLocator($urnResolver);
+    }
+
+    public function testGetSchema()
+    {
+        $this->assertEquals('xsd/path', $this->model->getSchema());
+    }
+
+    public function testGetPerFileSchema()
+    {
+        $this->assertEquals('xsd/path', $this->model->getPerFileSchema());
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml b/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml
new file mode 100644
index 0000000000000000000000000000000000000000..85b1d67e902e15a18b0d62a9f1dbc6184bb27352
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
+    <engine name="mysql">
+        <feature name="synonyms" support="1" />
+        <feature name="stopwords" support="0" />
+    </engine>
+    <engine name="other">
+        <feature name="synonyms" support="1" />
+        <feature name="stopwords" support="1" />
+    </engine>
+    <engine name="none" />
+</engines>
diff --git a/lib/internal/Magento/Framework/Search/etc/search_engine.xsd b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd
new file mode 100644
index 0000000000000000000000000000000000000000..3b754301c76de70cf36b3f973a70e99ff9c34ea9
--- /dev/null
+++ b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:element name="engines">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="engine" maxOccurs="unbounded">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="feature" minOccurs="0" maxOccurs="unbounded">
+                                <xs:complexType>
+                                    <xs:attribute name="name" type="xs:string" use="required" />
+                                    <xs:attribute name="support" type="xs:boolean" use="required" />
+                                </xs:complexType>
+                            </xs:element>
+                        </xs:sequence>
+                        <xs:attribute name="name" type="xs:string" use="required" />
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
diff --git a/pub/errors/processorFactory.php b/pub/errors/processorFactory.php
index f185cd1247933bee192363099b1d54aa98d6ed1a..44c8d874f51595ada0801f8094e8d0359d8c29fe 100644
--- a/pub/errors/processorFactory.php
+++ b/pub/errors/processorFactory.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Framework\Error;
 
-require_once __DIR__ . '/../../app/bootstrap.php';
+require_once realpath(__DIR__) . '/../../app/bootstrap.php';
 require_once 'processor.php';
 
 /**
diff --git a/pub/index.php b/pub/index.php
index b1177dcef99d41b663cebed03f427ee3339abc62..586ddc38a1c21b68514bc02c8581ebd63b92e200 100644
--- a/pub/index.php
+++ b/pub/index.php
@@ -10,7 +10,7 @@ use Magento\Framework\App\Bootstrap;
 use Magento\Framework\App\Filesystem\DirectoryList;
 
 try {
-    require __DIR__ . '/../app/bootstrap.php';
+    require realpath(__DIR__) . '/../app/bootstrap.php';
 } catch (\Exception $e) {
     echo <<<HTML
 <div style="font:12px/1.35em arial, helvetica, sans-serif;">
diff --git a/pub/static.php b/pub/static.php
index 7c1df3a8d85e28adbe42071f70742beebbd42e8c..35b9d9d833735b1a7cf43e8c50ad1dd725aa5452 100644
--- a/pub/static.php
+++ b/pub/static.php
@@ -6,7 +6,7 @@
  * See COPYING.txt for license details.
  */
 
-require __DIR__ . '/../app/bootstrap.php';
+require realpath(__DIR__) . '/../app/bootstrap.php';
 $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
 /** @var \Magento\Framework\App\StaticResource $app */
 $app = $bootstrap->createApplication('Magento\Framework\App\StaticResource');
diff --git a/setup/pub/magento/setup/select-version.js b/setup/pub/magento/setup/select-version.js
index b133fa59d46a1607294fbc105065be321e5a6d70..71e758568dcd16ee42ddf5fb97ad38a2a31815de 100644
--- a/setup/pub/magento/setup/select-version.js
+++ b/setup/pub/magento/setup/select-version.js
@@ -5,7 +5,7 @@
 
 'use strict';
 angular.module('select-version', ['ngStorage'])
-    .controller('selectVersionController', ['$scope', '$http', '$localStorage', function ($scope, $http, $localStorage) {
+    .controller('selectVersionController', ['$scope', '$http', '$localStorage', '$sce', function ($scope, $http, $localStorage, $sce) {
         $scope.packages = [{
             name: '',
             version: ''
@@ -13,6 +13,7 @@ angular.module('select-version', ['ngStorage'])
         $scope.upgradeReadyForNext = false;
         $scope.upgradeProcessed = false;
         $scope.upgradeProcessError = false;
+        $scope.upgradeProcessErrorMessage = '';
         $scope.componentsReadyForNext = true;
         $scope.componentsProcessed = false;
         $scope.componentsProcessError = false;
@@ -27,33 +28,39 @@ angular.module('select-version', ['ngStorage'])
         $http.get('index.php/select-version/systemPackage', {'responseType' : 'json'})
             .success(function (data) {
                 if (data.responseType != 'error') {
-                    $scope.selectedOption = [];
-                    $scope.versions = [];
-                    for (var i = 0; i < data.packages.length; i++) {
-                        angular.forEach(data.packages[i].versions, function (value, key) {
-                            $scope.versions.push({
-                                'versionInfo': angular.toJson({
-                                    'package': data.packages[i].package,
-                                    'version': value
-                                }), 'version': value
+                    if (data.packages.length <= 1) {
+                        $scope.upgradeProcessError = true;
+                        $scope.upgradeProcessErrorMessage = "You're already using the latest version, there's nothing for us to do.";
+                    } else {
+                        $scope.selectedOption = [];
+                        $scope.versions = [];
+                        for (var i = 0; i < data.packages.length; i++) {
+                            angular.forEach(data.packages[i].versions, function (value, key) {
+                                $scope.versions.push({
+                                    'versionInfo': angular.toJson({
+                                        'package': data.packages[i].package,
+                                        'version': value
+                                    }), 'version': value
+                                });
                             });
+                        }
+
+                        $scope.versions = $scope.versions.sort(function (a, b) {
+                            if (a.version.id < b.version.id) {
+                                return 1;
+                            }
+                            if (a.version.id > b.version.id) {
+                                return -1;
+                            }
+                            return 0;
                         });
+                        $scope.selectedOption = $scope.versions[0].versionInfo;
+                        $scope.upgradeReadyForNext = true;
                     }
 
-                    $scope.versions = $scope.versions.sort(function (a, b) {
-                        if (a.version.id < b.version.id) {
-                            return 1;
-                        }
-                        if (a.version.id > b.version.id) {
-                            return -1;
-                        }
-                        return 0;
-                    });
-                    $scope.selectedOption = $scope.versions[0].versionInfo;
-                    $scope.upgradeReadyForNext = true;
-
                 } else {
                     $scope.upgradeProcessError = true;
+                    $scope.upgradeProcessErrorMessage = $sce.trustAsHtml(data.error);
                 }
                 $scope.upgradeProcessed = true;
             })
diff --git a/setup/src/Magento/Setup/Model/SystemPackage.php b/setup/src/Magento/Setup/Model/SystemPackage.php
index 773f22212a3610445a55f6b9b1a7c6ae64157b41..952847f94491c761d0d31875a17d206ada6be4e4 100755
--- a/setup/src/Magento/Setup/Model/SystemPackage.php
+++ b/setup/src/Magento/Setup/Model/SystemPackage.php
@@ -59,13 +59,19 @@ class SystemPackage
         $systemPackages = [];
         $systemPackages = $this->getInstalledSystemPackages($systemPackages);
         if (empty($systemPackages)) {
-            throw new \RuntimeException('System packages not found');
+            // git cloned Magento does not include system package
+            throw new \RuntimeException(
+                'We\'re sorry, no components are available because you cloned the Magento 2 GitHub repository. ' .
+                'You must manually update components as discussed in the ' .
+                '<a href="http://devdocs.magento.com/guides/v2.0/install-gde/install/cli/dev_options.html">' .
+                'Installation Guide</a>.'
+            );
         }
         foreach ($systemPackages as $systemPackage) {
             $versions = [];
             $systemPackageInfo = $this->infoCommand->run($systemPackage);
             if (!$systemPackageInfo) {
-                throw new \RuntimeException('System package not found');
+                throw new \RuntimeException("We cannot retrieve information on $systemPackage.");
             }
 
             $versions = $this->getSystemPackageVersions($systemPackageInfo, $versions);
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php
index e5f521061a5466676fa4f0ca37c6ff67259ae307..9ce624511688ec2585e477f379b41733b900ff15 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php
@@ -50,7 +50,6 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase
      */
     private $composerInformation;
 
-
     public function setUp()
     {
         $this->composerAppFactory = $this->getMock(
@@ -178,7 +177,36 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \RuntimeException
-     * @expectedExceptionMessage System package not found
+     * @expectedExceptionMessage no components are available because you cloned the Magento 2 GitHub repository
+     */
+    public function testGetPackageVersionGitCloned()
+    {
+        $package = $this->getMock('\Composer\Package\Package', [], [], '', false);
+        $this->repository
+            ->expects($this->once())
+            ->method('getPackages')
+            ->willReturn([$package]);
+
+        $this->locker->expects($this->once())->method('getLockedRepository')->willReturn($this->repository);
+        $this->composerInformation->expects($this->any())->method('isSystemPackage')->willReturn(false);
+        $this->composer->expects($this->once())->method('getLocker')->willReturn($this->locker);
+        $this->magentoComposerApp->expects($this->once())->method('createComposer')->willReturn($this->composer);
+
+        $this->composerAppFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($this->magentoComposerApp);
+
+        $this->composerAppFactory->expects($this->once())
+            ->method('createInfoCommand')
+            ->willReturn($this->infoCommand);
+
+        $this->systemPackage = new SystemPackage($this->composerAppFactory, $this->composerInformation);
+        $this->systemPackage->getPackageVersions();
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     * @expectedExceptionMessage We cannot retrieve information on magento/product-community-edition.
      */
     public function testGetPackageVersionsFailed()
     {
@@ -200,10 +228,6 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase
         $this->composer->expects($this->once())->method('getLocker')->willReturn($this->locker);
         $this->magentoComposerApp->expects($this->once())->method('createComposer')->willReturn($this->composer);
 
-        $this->composerAppFactory->expects($this->once())
-            ->method('createInfoCommand')
-            ->willReturn($this->infoCommand);
-
         $this->composerAppFactory->expects($this->once())
             ->method('create')
             ->willReturn($this->magentoComposerApp);
diff --git a/setup/view/magento/setup/select-version.phtml b/setup/view/magento/setup/select-version.phtml
index 706983297e829678949b083cf2b5b7e19f853017..18cfc66d9aed1a8f8c26f39158e4e5fbdf6c2836 100644
--- a/setup/view/magento/setup/select-version.phtml
+++ b/setup/view/magento/setup/select-version.phtml
@@ -39,9 +39,7 @@
         <span class="message-text">Checking for a new version...</span>
     </div>
     <div class="message message-error" ng-show="upgradeProcessError">
-            <span class="message-text">
-                Sorry, we can't take that action right now.
-            </span>
+            <span class="message-text" ng-bind-html="upgradeProcessErrorMessage"></span>
     </div>
     <div class="row" ng-show="upgradeProcessed && !upgradeProcessError">
         <div class="col-m-3">