diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php
index d5cf7e86c4b8ae8c301a2993893a9d31d615e7e5..0e9383d4e63c6e11997e65389bc435cceb5b7af8 100644
--- a/app/code/Magento/Backend/Model/Auth/Session.php
+++ b/app/code/Magento/Backend/Model/Auth/Session.php
@@ -171,26 +171,13 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
     }
 
     /**
-     * Set session UpdatedAt to current time and update cookie expiration time
+     * Set session UpdatedAt to current time
      *
      * @return void
      */
     public function prolong()
     {
-        $lifetime = $this->_config->getValue(self::XML_PATH_SESSION_LIFETIME);
-        $currentTime = time();
-
-        $this->setUpdatedAt($currentTime);
-        $cookieValue = $this->cookieManager->getCookie($this->getName());
-        if ($cookieValue) {
-            $cookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata()
-                ->setDuration($lifetime)
-                ->setPath($this->sessionConfig->getCookiePath())
-                ->setDomain($this->sessionConfig->getCookieDomain())
-                ->setSecure($this->sessionConfig->getCookieSecure())
-                ->setHttpOnly($this->sessionConfig->getCookieHttpOnly());
-            $this->cookieManager->setPublicCookie($this->getName(), $cookieValue, $cookieMetadata);
-        }
+        $this->setUpdatedAt(time());
     }
 
     /**
diff --git a/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccb5c65a8c5080984ff72627760a795e2507cbde
--- /dev/null
+++ b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Backend\Model\Config\SessionLifetime;
+
+use Magento\Framework\App\Config\Value;
+use Magento\Framework\Exception\LocalizedException;
+
+/**
+ * Backend model for the admin/security/session_lifetime configuration field. Validates session lifetime.
+ */
+class BackendModel extends Value
+{
+    /** Maximum dmin session lifetime; 1 year*/
+    const MAX_LIFETIME = 31536000;
+
+    /** Minimum admin session lifetime */
+    const MIN_LIFETIME = 60;
+
+    public function beforeSave()
+    {
+        $value = (int) $this->getValue();
+        if ($value > self::MAX_LIFETIME) {
+            throw new LocalizedException(
+                __('Admin session lifetime must be less than or equal to 31536000 seconds (one year)')
+            );
+        } else if ($value < self::MIN_LIFETIME) {
+            throw new LocalizedException(
+                __('Admin session lifetime must be greater than or equal to 60 seconds')
+            );
+        }
+        return parent::beforeSave();
+    }
+}
diff --git a/app/code/Magento/Backend/Model/Session/AdminConfig.php b/app/code/Magento/Backend/Model/Session/AdminConfig.php
index 9dced89caf30d572aeb36fd149850ef36acac015..8edec773402648d9be0653aab9e2496f67286d81 100644
--- a/app/code/Magento/Backend/Model/Session/AdminConfig.php
+++ b/app/code/Magento/Backend/Model/Session/AdminConfig.php
@@ -14,8 +14,6 @@ use Magento\Framework\Session\Config;
 
 /**
  * Magento Backend session configuration
- *
- * @method Config setSaveHandler()
  */
 class AdminConfig extends Config
 {
@@ -107,4 +105,14 @@ class AdminConfig extends Config
         $cookiePath = $baseUrl . $backendApp->getCookiePath();
         return $cookiePath;
     }
+
+    /**
+     * Set session cookie lifetime to session duration
+     *
+     * @return $this
+     */
+    protected function configureCookieLifetime()
+    {
+        return $this->setCookieLifetime(0);
+    }
 }
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php
index 3e41177e72c0cae37f1f4324caa310d63e471e2c..b5b3fed369f326ef9724fee32f003b6ea42f359d 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php
@@ -162,67 +162,7 @@ class SessionTest extends \PHPUnit_Framework_TestCase
 
     public function testProlong()
     {
-        $name = session_name();
-        $cookie = 'cookie';
-        $lifetime = 900;
-        $path = '/';
-        $domain = 'magento2';
-        $secure = true;
-        $httpOnly = true;
-
-        $cookieMetadata = $this->getMock('Magento\Framework\Stdlib\Cookie\PublicCookieMetadata');
-        $cookieMetadata->expects($this->once())
-            ->method('setDuration')
-            ->with($lifetime)
-            ->will($this->returnSelf());
-        $cookieMetadata->expects($this->once())
-            ->method('setPath')
-            ->with($path)
-            ->will($this->returnSelf());
-        $cookieMetadata->expects($this->once())
-            ->method('setDomain')
-            ->with($domain)
-            ->will($this->returnSelf());
-        $cookieMetadata->expects($this->once())
-            ->method('setSecure')
-            ->with($secure)
-            ->will($this->returnSelf());
-        $cookieMetadata->expects($this->once())
-            ->method('setHttpOnly')
-            ->with($httpOnly)
-            ->will($this->returnSelf());
-
-        $this->cookieMetadataFactory->expects($this->once())
-            ->method('createPublicCookieMetadata')
-            ->will($this->returnValue($cookieMetadata));
-
-        $this->cookieManager->expects($this->once())
-            ->method('getCookie')
-            ->with($name)
-            ->will($this->returnValue($cookie));
-        $this->cookieManager->expects($this->once())
-            ->method('setPublicCookie')
-            ->with($name, $cookie, $cookieMetadata);
-
-        $this->config->expects($this->once())
-            ->method('getValue')
-            ->with(\Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME)
-            ->will($this->returnValue($lifetime));
-        $this->sessionConfig->expects($this->once())
-            ->method('getCookiePath')
-            ->will($this->returnValue($path));
-        $this->sessionConfig->expects($this->once())
-            ->method('getCookieDomain')
-            ->will($this->returnValue($domain));
-        $this->sessionConfig->expects($this->once())
-            ->method('getCookieSecure')
-            ->will($this->returnValue($secure));
-        $this->sessionConfig->expects($this->once())
-            ->method('getCookieHttpOnly')
-            ->will($this->returnValue($httpOnly));
-
         $this->session->prolong();
-
         $this->assertLessThanOrEqual(time(), $this->session->getUpdatedAt());
     }
 
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php
new file mode 100755
index 0000000000000000000000000000000000000000..a26910a45baade098d5322eb655469f8fd106e8e
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Backend\Test\Unit\Model\Config\SessionLifetime;
+
+use Magento\Backend\Model\Config\SessionLifetime\BackendModel;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class BackendModelTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider adminSessionLifetimeDataProvider
+     */
+    public function testBeforeSave($value, $errorMessage = null)
+    {
+        /** @var BackendModel $model */
+        $model = (new ObjectManager($this))->getObject('Magento\Backend\Model\Config\SessionLifetime\BackendModel');
+        if ($errorMessage !== null) {
+            $this->setExpectedException('\Magento\Framework\Exception\LocalizedException', $errorMessage);
+        }
+        $model->setValue($value);
+        $model->beforeSave();
+    }
+
+    public function adminSessionLifetimeDataProvider()
+    {
+        return [
+            [
+                BackendModel::MIN_LIFETIME - 1,
+                'Admin session lifetime must be greater than or equal to 60 seconds'
+            ],
+            [
+                BackendModel::MAX_LIFETIME + 1,
+                'Admin session lifetime must be less than or equal to 31536000 seconds (one year)'
+            ],
+            [
+                900
+            ]
+        ];
+    }
+}
diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml
index f7b9bd917b030a232cc3584adcda2aa9653e785c..65a1872ed4999a765d002a98dd9b31ea73dbf168 100644
--- a/app/code/Magento/Backend/etc/adminhtml/system.xml
+++ b/app/code/Magento/Backend/etc/adminhtml/system.xml
@@ -388,7 +388,8 @@
                 </field>
                 <field id="session_lifetime" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0">
                     <label>Admin Session Lifetime (seconds)</label>
-                    <comment>Values less than 60 are ignored.</comment>
+                    <comment>Please enter at least 60 and at most 31536000 (one year).</comment>
+                    <backend_model>Magento\Backend\Model\Config\SessionLifetime\BackendModel</backend_model>
                     <validate>validate-digits</validate>
                 </field>
             </group>
diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv
index 0c5586948801fcf220e16302f0d15949d6b68f6e..a704cab6687c3ff62f0f4e239c0edc0669688b74 100644
--- a/app/code/Magento/Backend/i18n/en_US.csv
+++ b/app/code/Magento/Backend/i18n/en_US.csv
@@ -556,7 +556,9 @@ Security,Security
 "Add Secret Key to URLs","Add Secret Key to URLs"
 "Login is Case Sensitive","Login is Case Sensitive"
 "Admin Session Lifetime (seconds)","Admin Session Lifetime (seconds)"
-"Values less than 60 are ignored.","Values less than 60 are ignored."
+"Please enter at least 60 and at most 31536000 (one year).","Please enter at least 60 and at most 31536000 (one year)."
+"Admin session lifetime must be less than or equal to 31536000 seconds (one year)","Admin session lifetime must be less than or equal to 31536000 seconds (one year)"
+"Admin session lifetime must be greater than or equal to 60 seconds","Admin session lifetime must be greater than or equal to 60 seconds"
 Web,Web
 "Url Options","Url Options"
 "Add Store Code to Urls","Add Store Code to Urls"
diff --git a/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php b/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
index 796af9fe13f418e5aa9df3979a583ab9f947bc4b..b6641286fd4507b2dd0990cba7ba239f4664c1fa 100644
--- a/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
+++ b/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
@@ -130,6 +130,7 @@ class Bundle
             if ((bool)$optionData['delete']) {
                 continue;
             }
+
             $option = $this->optionFactory->create(['data' => $optionData]);
             $option->setSku($product->getSku());
             $option->setOptionId(null);
diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php
index dd246101c0f3517c38d042cbc02c05c0ef6e0116..0e26022d1e8a6590377c09e11601183c09ef9d0c 100644
--- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php
+++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php
@@ -158,20 +158,7 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\
         );
         $this->_addAttributeToSelect($select, 'status', "e.$linkField", 'cs.store_id', $condition);
 
-        if ($this->_isManageStock()) {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
-                '1',
-                'cisi.is_in_stock'
-            );
-        } else {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
-                'cisi.is_in_stock',
-                '1'
-            );
-        }
-
+        $statusExpr = $this->getStatusExpression($connection);
         $select->columns(
             [
                 'status' => $connection->getLeastSql(
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php
index 07a86a028ed44584e8370199b770246806e6feea..d3c27a6b3ed1a1c9bbc2b2b1279bc9dac8ebf2c5 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php
@@ -20,6 +20,11 @@ class Inventory extends \Magento\Backend\Block\Widget implements \Magento\Backen
      */
     protected $stockConfiguration;
 
+    /**
+     * @var array
+     */
+    protected $disabledFields = [];
+
     /**
      * @param \Magento\Backend\Block\Template\Context $context
      * @param \Magento\CatalogInventory\Model\Source\Backorders $backorders
@@ -112,4 +117,14 @@ class Inventory extends \Magento\Backend\Block\Widget implements \Magento\Backen
     {
         return false;
     }
+
+    /**
+     * @param string $fieldName
+     * @return bool
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function isAvailable($fieldName)
+    {
+        return true;
+    }
 }
diff --git a/app/code/Magento/Catalog/Block/Category/Plugin/PriceBoxTags.php b/app/code/Magento/Catalog/Block/Category/Plugin/PriceBoxTags.php
new file mode 100644
index 0000000000000000000000000000000000000000..65c9b3ad624ee6c4ac4119c1c38064ea4943915a
--- /dev/null
+++ b/app/code/Magento/Catalog/Block/Category/Plugin/PriceBoxTags.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Block\Category\Plugin;
+
+use Magento\Catalog\Model\Product;
+use Magento\Customer\Model\Session;
+use Magento\Framework\App\ScopeResolverInterface;
+use Magento\Framework\Pricing\PriceCurrencyInterface;
+use Magento\Framework\Pricing\Render\PriceBox;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
+
+class PriceBoxTags
+{
+    /**
+     * @var TimezoneInterface
+     */
+    protected $dateTime;
+
+    /**
+     * @var \Magento\Customer\Model\Session
+     */
+    protected $customerSession;
+
+    /**
+     * @var PriceCurrencyInterface
+     */
+    private $priceCurrency;
+    
+    /**
+     * @var ScopeResolverInterface
+     */
+    private $scopeResolver;
+
+    /**
+     * PriceBoxTags constructor.
+     * @param PriceCurrencyInterface $priceCurrency
+     * @param TimezoneInterface $dateTime
+     * @param ScopeResolverInterface $scopeResolver
+     * @param Session $customerSession
+     */
+    public function __construct(
+        PriceCurrencyInterface $priceCurrency,
+        TimezoneInterface $dateTime,
+        ScopeResolverInterface $scopeResolver,
+        Session $customerSession
+    ) {
+        $this->dateTime = $dateTime;
+        $this->customerSession = $customerSession;
+        $this->priceCurrency = $priceCurrency;
+        $this->scopeResolver = $scopeResolver;
+    }
+
+    /**
+     * @param PriceBox $subject
+     * @param string $result
+     * @return string
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterGetCacheKey(PriceBox $subject, $result)
+    {
+        return implode(
+            '-',
+            [
+                $result,
+                $this->priceCurrency->getCurrencySymbol(),
+                $this->dateTime->scopeDate($this->scopeResolver->getScope()->getId())->format('Ymd'),
+                $this->scopeResolver->getScope()->getId(),
+                $this->customerSession->getCustomerGroupId(),
+            ]
+        );
+    }
+}
diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php
index b0684d90546f184c3e185c44d8d5e8e711be7f2f..e9266ee0216a5396ca9ef9eff3710fbd406d8c05 100644
--- a/app/code/Magento/Catalog/Controller/Category/View.php
+++ b/app/code/Magento/Catalog/Controller/Category/View.php
@@ -172,13 +172,15 @@ class View extends \Magento\Framework\App\Action\Action
             if ($settings->getPageLayout()) {
                 $page->getConfig()->setPageLayout($settings->getPageLayout());
             }
+
+            $hasChildren = $category->hasChildren();
             if ($category->getIsAnchor()) {
-                $type = $category->hasChildren() ? 'layered' : 'layered_without_children';
+                $type = $hasChildren ? 'layered' : 'layered_without_children';
             } else {
-                $type = $category->hasChildren() ? 'default' : 'default_without_children';
+                $type = $hasChildren ? 'default' : 'default_without_children';
             }
 
-            if (!$category->hasChildren()) {
+            if (!$hasChildren) {
                 // Two levels removed from parent.  Need to add default page type.
                 $parentType = strtok($type, '_');
                 $page->addPageLayoutHandles(['type' => $parentType]);
diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php
index dd89956d3a66d8c531dc82bdb659c3db55305697..4efa1b08969929f59ea63d1dd4595a046af0c216 100644
--- a/app/code/Magento/Catalog/Model/Category.php
+++ b/app/code/Magento/Catalog/Model/Category.php
@@ -1115,7 +1115,10 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements
         $identities = [
             self::CACHE_TAG . '_' . $this->getId(),
         ];
-        if ($this->hasDataChanges() || $this->isDeleted()) {
+        if (!$this->getId() || $this->hasDataChanges()
+            || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)
+        ) {
+            $identities[] = self::CACHE_TAG;
             $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId();
         }
         return $identities;
diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php
index 479f02796b57cbcfd15070ee3585fd0b3adf3b2c..8fa599af1ce77ed01a652551a81d89aae857245d 100644
--- a/app/code/Magento/Catalog/Model/Category/DataProvider.php
+++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php
@@ -253,7 +253,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
                 $meta[$code]['validation'] = $rules;
             }
 
-            $meta[$code]['scope_label'] = $this->getScopeLabel($attribute);
+            $meta[$code]['scopeLabel'] = $this->getScopeLabel($attribute);
             $meta[$code]['componentType'] = Field::NAME;
         }
 
diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
index fdf80681159cce87ab47cb677b5cb34508a556ce..1c4c8533ef2465d4d1520dac8c47aa921c39347f 100644
--- a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
+++ b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
@@ -376,7 +376,7 @@ abstract class AbstractFilter extends \Magento\Framework\DataObject implements F
      */
     protected function getAttributeIsFilterable($attribute)
     {
-        return $attribute->getIsFilterable();
+        return (int)$attribute->getIsFilterable();
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
index 13b119e93f5e8f46f60c18c2e0e85cc60182c801..b33f10728a29f9f0527103b908b9abff5fca7e04 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
@@ -166,7 +166,7 @@ abstract class AbstractGroupPrice extends Price
             if (!empty($priceRow['delete'])) {
                 continue;
             }
-            $compare = join(
+            $compare = implode(
                 '-',
                 array_merge(
                     [$priceRow['website_id'], $priceRow['cust_group']],
@@ -191,7 +191,7 @@ abstract class AbstractGroupPrice extends Price
             if ($origPrices) {
                 foreach ($origPrices as $price) {
                     if ($price['website_id'] == 0) {
-                        $compare = join(
+                        $compare = implode(
                             '-',
                             array_merge(
                                 [$price['website_id'], $price['cust_group']],
@@ -215,7 +215,7 @@ abstract class AbstractGroupPrice extends Price
                 continue;
             }
 
-            $globalCompare = join(
+            $globalCompare = implode(
                 '-',
                 array_merge([0, $priceRow['cust_group']], $this->_getAdditionalUniqueFields($priceRow))
             );
@@ -243,7 +243,7 @@ abstract class AbstractGroupPrice extends Price
         $data = [];
         $price = $this->_catalogProductType->priceFactory($productTypeId);
         foreach ($priceData as $v) {
-            $key = join('-', array_merge([$v['cust_group']], $this->_getAdditionalUniqueFields($v)));
+            $key = implode('-', array_merge([$v['cust_group']], $this->_getAdditionalUniqueFields($v)));
             if ($v['website_id'] == $websiteId) {
                 $data[$key] = $v;
                 $data[$key]['website_price'] = $v['price'];
@@ -316,7 +316,7 @@ abstract class AbstractGroupPrice extends Price
         $isGlobal = $this->getAttribute()->isScopeGlobal() || $websiteId == 0;
 
         $priceRows = $object->getData($this->getAttribute()->getName());
-        if ($priceRows === null) {
+        if (null === $priceRows) {
             return $this;
         }
 
@@ -330,7 +330,7 @@ abstract class AbstractGroupPrice extends Price
         }
         foreach ($origPrices as $data) {
             if ($data['website_id'] > 0 || $data['website_id'] == '0' && $isGlobal) {
-                $key = join(
+                $key = implode(
                     '-',
                     array_merge(
                         [$data['website_id'], $data['cust_group']],
@@ -361,7 +361,7 @@ abstract class AbstractGroupPrice extends Price
                 continue;
             }
 
-            $key = join(
+            $key = implode(
                 '-',
                 array_merge([$data['website_id'], $data['cust_group']], $this->_getAdditionalUniqueFields($data))
             );
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
index 2cafca07cbfe0aeb4572dab9013268820a5eef74..5aec262a6911d28fef55ce3eeae92a1472289aa9 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
@@ -49,6 +49,11 @@ class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInter
      */
     protected $searchCriteriaBuilder;
 
+    /**
+     * @var \Magento\Catalog\Api\ProductAttributeOptionManagementInterface
+     */
+    private $optionManagement;
+
     /**
      * @param \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource
      * @param \Magento\Catalog\Helper\Product $productHelper
@@ -57,6 +62,7 @@ class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInter
      * @param \Magento\Eav\Model\Config $eavConfig
      * @param \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory $validatorFactory
      * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param \Magento\Catalog\Api\ProductAttributeOptionManagementInterface $optionManagement
      */
     public function __construct(
         \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource,
@@ -65,7 +71,8 @@ class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInter
         \Magento\Eav\Api\AttributeRepositoryInterface $eavAttributeRepository,
         \Magento\Eav\Model\Config $eavConfig,
         \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory $validatorFactory,
-        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
+        \Magento\Catalog\Api\ProductAttributeOptionManagementInterface $optionManagement
     ) {
         $this->attributeResource = $attributeResource;
         $this->productHelper = $productHelper;
@@ -74,6 +81,7 @@ class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInter
         $this->eavConfig = $eavConfig;
         $this->inputtypeValidatorFactory = $validatorFactory;
         $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->optionManagement = $optionManagement;
     }
 
     /**
@@ -171,7 +179,10 @@ class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInter
             $attribute->setIsUserDefined(1);
         }
         $this->attributeResource->save($attribute);
-        return $attribute;
+        foreach ($attribute->getOptions() as $option) {
+            $this->optionManagement->add($attribute->getAttributeCode(), $option);
+        }
+        return $this->get($attribute->getAttributeCode());
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
index cde304b004ac497e35502954291fda86a499533e..76729cdb7458c4869664feaf0b9e7897a932486a 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
@@ -818,4 +818,30 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements
         $this->_eavConfig->clear();
         return parent::afterDelete();
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            ['_indexerEavProcessor', '_productFlatIndexerProcessor', '_productFlatIndexerHelper', 'attrLockValidator']
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_indexerEavProcessor = $objectManager->get(\Magento\Catalog\Model\Indexer\Product\Flat\Processor::class);
+        $this->_productFlatIndexerProcessor = $objectManager->get(
+            \Magento\Catalog\Model\Indexer\Product\Eav\Processor::class
+        );
+        $this->_productFlatIndexerHelper = $objectManager->get(\Magento\Catalog\Helper\Product\Flat\Indexer::class);
+        $this->attrLockValidator = $objectManager->get(LockValidatorInterface::class);
+    }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/InventoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/InventoryTest.php
index 9c8ea48b531eb2cd9a5b26c250ef5d9106b32ff1..48ebd822875a4d025d3b6b73c4b8a04381360f37 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/InventoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/InventoryTest.php
@@ -177,4 +177,14 @@ class InventoryTest extends \PHPUnit_Framework_TestCase
     {
         $this->assertFalse($this->inventory->isHidden());
     }
+
+    /**
+     * Run test isEnabled method
+     *
+     * @return void
+     */
+    public function testIsEnabled()
+    {
+        $this->assertEquals(true, $this->inventory->isAvailable('field'));
+    }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php
index 1cf511043804620a61c4c80fb338e0126b160230..c4cc6f48c5607baeed7b157d0f2777c47be4194d 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php
@@ -63,6 +63,11 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $searchResultMock;
 
+    /**
+     * @var \Magento\Eav\Api\AttributeOptionManagementInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $optionManagementMock;
+
     protected function setUp()
     {
         $this->attributeResourceMock =
@@ -99,6 +104,8 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase
                 [],
                 '',
                 false);
+        $this->optionManagementMock =
+            $this->getMock('\Magento\Catalog\Api\ProductAttributeOptionManagementInterface', [], [], '', false);
 
         $this->model = new Repository(
             $this->attributeResourceMock,
@@ -107,7 +114,8 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase
             $this->eavAttributeRepositoryMock,
             $this->eavConfigMock,
             $this->validatorFactoryMock,
-            $this->searchCriteriaBuilderMock
+            $this->searchCriteriaBuilderMock,
+            $this->optionManagementMock
         );
     }
 
diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php
index 8719e6f3d60994d9780a67a70b8e53c60a445c38..43dc002857c6c20f0ea6d362622681ff9e648760 100644
--- a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php
@@ -81,6 +81,9 @@ class FinalPriceBoxTest extends \PHPUnit_Framework_TestCase
             ->method('getBlock')
             ->will($this->returnValue($this->priceBox));
 
+        $cacheState = $this->getMockBuilder(\Magento\Framework\App\Cache\StateInterface::class)
+            ->getMockForAbstractClass();
+
         $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface');
         $context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false);
         $context->expects($this->any())
@@ -98,6 +101,9 @@ class FinalPriceBoxTest extends \PHPUnit_Framework_TestCase
         $context->expects($this->any())
             ->method('getScopeConfig')
             ->will($this->returnValue($scopeConfigMock));
+        $context->expects($this->any())
+            ->method('getCacheState')
+            ->will($this->returnValue($cacheState));
 
         $this->rendererPool = $this->getMockBuilder('Magento\Framework\Pricing\Render\RendererPool')
             ->disableOriginalConstructor()
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
index b9bf7aba3aba4e1c2b28ffb084fedde226a29323..9370390ad7bec3fbb6c5ed0c0e95e4848734acd2 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
@@ -528,7 +528,8 @@ class Eav extends AbstractModifier
         }
 
         // TODO: getAttributeModel() should not be used when MAGETWO-48284 is complete
-        if (($rules = $this->eavValidationRules->build($attribute, $meta))) {
+        $childData = $this->arrayManager->get($configPath, $meta, []);
+        if (($rules = $this->eavValidationRules->build($this->getAttributeModel($attribute), $childData))) {
             $meta = $this->arrayManager->merge($configPath, $meta, [
                 'validation' => $rules,
             ]);
diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml
index 07a5986dd17080ec208389ecb552ecc3b0b57b94..d9e46fb2517aede84bd5cf6eb9063619f7977cf0 100644
--- a/app/code/Magento/Catalog/etc/frontend/di.xml
+++ b/app/code/Magento/Catalog/etc/frontend/di.xml
@@ -60,4 +60,7 @@
             </argument>
         </arguments>
     </type>
+    <type name="\Magento\Framework\Pricing\Render\PriceBox">
+        <plugin name="catalog_price_box_key" type="Magento\Catalog\Block\Category\Plugin\PriceBoxTags" />
+    </type>
 </config>
diff --git a/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php b/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php
index 93224e80fe3efdf07c602ec02d57bd36274b3db6..ffe5f6559c35dedcdda881b31d649a88d821c52c 100644
--- a/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php
+++ b/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php
@@ -25,11 +25,6 @@ abstract class AbstractStockqty extends \Magento\Framework\View\Element\Template
      */
     protected $_coreRegistry;
 
-    /**
-     * @var \Magento\CatalogInventory\Api\StockStateInterface
-     */
-    protected $stockState;
-
     /**
      * @var \Magento\CatalogInventory\Api\StockRegistryInterface
      */
@@ -38,19 +33,16 @@ abstract class AbstractStockqty extends \Magento\Framework\View\Element\Template
     /**
      * @param \Magento\Framework\View\Element\Template\Context $context
      * @param \Magento\Framework\Registry $registry
-     * @param \Magento\CatalogInventory\Api\StockStateInterface $stockState
      * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
      * @param array $data
      */
     public function __construct(
         \Magento\Framework\View\Element\Template\Context $context,
         \Magento\Framework\Registry $registry,
-        \Magento\CatalogInventory\Api\StockStateInterface $stockState,
         \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
         array $data = []
     ) {
         $this->_coreRegistry = $registry;
-        $this->stockState = $stockState;
         $this->stockRegistry = $stockRegistry;
 
         parent::__construct($context, $data);
@@ -92,7 +84,7 @@ abstract class AbstractStockqty extends \Magento\Framework\View\Element\Template
      */
     public function getProductStockQty($product)
     {
-        return $this->stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId());
+        return $this->stockRegistry->getStockStatus($product->getId(), $product->getStore()->getWebsiteId())->getQty();
     }
 
     /**
diff --git a/app/code/Magento/CatalogInventory/Helper/Stock.php b/app/code/Magento/CatalogInventory/Helper/Stock.php
index 8a227f53ebc1fce93a93a809ab5a0c1e887c06c4..8f7bb78776cba151fd7e3393d4144adb6c53e4cd 100644
--- a/app/code/Magento/CatalogInventory/Helper/Stock.php
+++ b/app/code/Magento/CatalogInventory/Helper/Stock.php
@@ -139,8 +139,16 @@ class Stock
      */
     public function addIsInStockFilterToCollection($collection)
     {
-        $resource = $this->getStockStatusResource();
-        $resource->addIsInStockFilterToCollection($collection);
+        $stockFlag = 'has_stock_status_filter';
+        if (!$collection->hasFlag($stockFlag)) {
+            $isShowOutOfStock = $this->scopeConfig->getValue(
+                \Magento\CatalogInventory\Model\Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
+                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+            );
+            $resource = $this->getStockStatusResource();
+            $resource->addStockDataToCollection($collection, !$isShowOutOfStock);
+            $collection->setFlag($stockFlag, true);
+        }
     }
 
     /**
diff --git a/app/code/Magento/CatalogInventory/Model/AddStockStatusToCollection.php b/app/code/Magento/CatalogInventory/Model/AddStockStatusToCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..fdf613f5506a9aa6796881371194833c7a01318c
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/AddStockStatusToCollection.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogInventory\Model;
+
+use Magento\Catalog\Model\ResourceModel\Product\Collection;
+
+/**
+ * Catalog inventory module plugin
+ */
+class AddStockStatusToCollection
+{
+    /**
+     * @var \Magento\CatalogInventory\Helper\Stock
+     */
+    protected $stockHelper;
+    
+    /**
+     * @param \Magento\CatalogInventory\Model\Configuration $configuration
+     * @param \Magento\CatalogInventory\Helper\Stock $stockHelper
+     */
+    public function __construct(
+        \Magento\CatalogInventory\Helper\Stock $stockHelper
+    ) {
+        $this->stockHelper = $stockHelper;
+    }
+
+    /**
+     * @param Collection $productCollection
+     * @param bool $printQuery
+     * @param bool $logQuery
+     * @return array
+     */
+    public function beforeLoad(Collection $productCollection, $printQuery = false, $logQuery = false)
+    {
+        $this->stockHelper->addIsInStockFilterToCollection($productCollection);
+        return [$printQuery, $logQuery];
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
index c31e0f02c955c465e975112c36e38387904ad85a..8de183aeb391e42b893e72bd77293a0ff080d1cb 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
@@ -7,6 +7,8 @@
 namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock;
 
 use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer;
+use Magento\CatalogInventory\Model\Stock;
+use Magento\Framework\DB\Adapter\AdapterInterface;
 
 /**
  * CatalogInventory Default Stock Status Indexer Resource Model
@@ -35,14 +37,20 @@ class DefaultStock extends AbstractIndexer implements StockInterface
      */
     protected $_scopeConfig;
 
+    /**
+     * @var QueryProcessorComposite
+     */
+    private $queryProcessorComposite;
+
     /**
      * Class constructor
      *
      * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
      * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
      * @param \Magento\Eav\Model\Config $eavConfig
-     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
+     * @param QueryProcessorComposite $queryProcessorComposite
      * @param string $connectionName
      */
     public function __construct(
@@ -51,9 +59,11 @@ class DefaultStock extends AbstractIndexer implements StockInterface
         \Magento\Eav\Model\Config $eavConfig,
         \Magento\Framework\Model\Entity\MetadataPool $metadataPool,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
+        QueryProcessorComposite $queryProcessorComposite,
         $connectionName = null
     ) {
         $this->_scopeConfig = $scopeConfig;
+        $this->queryProcessorComposite = $queryProcessorComposite;
         parent::__construct($context, $tableStrategy, $eavConfig, $metadataPool, $connectionName);
     }
 
@@ -188,9 +198,10 @@ class DefaultStock extends AbstractIndexer implements StockInterface
             ['cisi' => $this->getTable('cataloginventory_stock_item')],
             'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id',
             []
-        )->columns(['qty' => $qtyExpr])
+        )->columns(['qty' => new \Zend_Db_Expr('SUM(' . $qtyExpr . ')')])
             ->where('cw.website_id != 0')
-            ->where('e.type_id = ?', $this->getTypeId());
+            ->where('e.type_id = ?', $this->getTypeId())
+            ->group('e.entity_id');
 
         // add limitation of status
         $condition = $connection->quoteInto(
@@ -205,21 +216,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface
             $condition
         );
 
-        if ($this->_isManageStock()) {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
-                1,
-                'cisi.is_in_stock'
-            );
-        } else {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
-                'cisi.is_in_stock',
-                1
-            );
-        }
-
-        $select->columns(['status' => $statusExpr]);
+        $select->columns(['status' => $this->getStatusExpression($connection, true)]);
 
         if ($entityIds !== null) {
             $select->where('e.entity_id IN(?)', $entityIds);
@@ -238,6 +235,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface
     {
         $connection = $this->getConnection();
         $select = $this->_getStockStatusSelect($entityIds);
+        $select = $this->queryProcessorComposite->processQuery($select, $entityIds);
         $query = $select->insertFromSelect($this->getIdxTable());
         $connection->query($query);
 
@@ -254,6 +252,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface
     {
         $connection = $this->getConnection();
         $select = $this->_getStockStatusSelect($entityIds, true);
+        $select = $this->queryProcessorComposite->processQuery($select, $entityIds, true);
         $query = $connection->query($select);
 
         $i = 0;
@@ -263,7 +262,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface
             $data[] = [
                 'product_id' => (int)$row['entity_id'],
                 'website_id' => (int)$row['website_id'],
-                'stock_id' => (int)$row['stock_id'],
+                'stock_id' => Stock::DEFAULT_STOCK_ID,
                 'qty' => (double)$row['qty'],
                 'stock_status' => (int)$row['status'],
             ];
@@ -306,4 +305,28 @@ class DefaultStock extends AbstractIndexer implements StockInterface
     {
         return $this->tableStrategy->getTableName('cataloginventory_stock_status');
     }
+
+    /**
+     * @param AdapterInterface $connection
+     * @param bool $isAggregate
+     * @return mixed
+     */
+    protected function getStatusExpression(AdapterInterface $connection, $isAggregate = false)
+    {
+        $isInStockExpression = $isAggregate ? 'MAX(cisi.is_in_stock)' : 'cisi.is_in_stock';
+        if ($this->_isManageStock()) {
+            $statusExpr = $connection->getCheckSql(
+                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
+                1,
+                $isInStockExpression
+            );
+        } else {
+            $statusExpr = $connection->getCheckSql(
+                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
+                $isInStockExpression,
+                1
+            );
+        }
+        return $statusExpr;
+    }
 }
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorComposite.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorComposite.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b89df82573ae4881ae88d89eb7b84311ee5eb42
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorComposite.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock;
+
+class QueryProcessorComposite implements QueryProcessorInterface
+{
+    /**
+     * @var array
+     */
+    private $queryProcessors;
+
+    /**
+     * QueryProcessorPool constructor.
+     * @param QueryProcessorInterface[] $queryProcessors
+     */
+    public function __construct(array $queryProcessors = [])
+    {
+        $this->queryProcessors = $queryProcessors;
+    }
+
+    /**
+     * @param \Magento\Framework\DB\Select $select
+     * @param null|array $entityIds
+     * @param bool $usePrimaryTable
+     * @return \Magento\Framework\DB\Select
+     */
+    public function processQuery(\Magento\Framework\DB\Select $select, $entityIds = null, $usePrimaryTable = false)
+    {
+        foreach ($this->queryProcessors as $queryProcessor) {
+            $select = $queryProcessor->processQuery($select, $entityIds, $usePrimaryTable);
+        }
+        return $select;
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..4815e173eba03671a59c9724164c9a59a5d2187c
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock;
+
+use Magento\Framework\DB\Select;
+
+interface QueryProcessorInterface
+{
+    /**
+     * @param Select $select
+     * @param null|array $entityIds
+     * @param bool $usePrimaryTable
+     * @return Select
+     */
+    public function processQuery(Select $select, $entityIds = null, $usePrimaryTable = false);
+}
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
index be7e1ff2d1b64487e09ac206d40f6deecfb15f90..5fe199905837b553d16f02fb01bf4be5a07c01be 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
@@ -198,12 +198,45 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         $select->joinLeft(
             ['stock_status' => $this->getMainTable()],
             'e.entity_id = stock_status.product_id AND stock_status.website_id=' . $websiteId,
-            ['salable' => 'stock_status.stock_status']
+            ['is_salable' => 'stock_status.stock_status']
         );
 
         return $this;
     }
 
+    /**
+     * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+     * @param bool $isFilterInStock
+     * @return \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+     */
+    public function addStockDataToCollection($collection, $isFilterInStock)
+    {
+        $websiteId = $this->_storeManager->getStore($collection->getStoreId())->getWebsiteId();
+        $joinCondition = $this->getConnection()->quoteInto(
+            'e.entity_id = stock_status_index.product_id' . ' AND stock_status_index.website_id = ?',
+            $websiteId
+        );
+
+        $joinCondition .= $this->getConnection()->quoteInto(
+            ' AND stock_status_index.stock_id = ?',
+            Stock::DEFAULT_STOCK_ID
+        );
+
+        $collection->getSelect()->join(
+            ['stock_status_index' => $this->getMainTable()],
+            $joinCondition,
+            ['is_salable' => 'stock_status']
+        );
+
+        if ($isFilterInStock) {
+            $collection->getSelect()->where(
+                'stock_status_index.stock_status = ?',
+                Stock\Status::STATUS_IN_STOCK
+            );
+        }
+        return $collection;
+    }
+
     /**
      * Add only is in stock products filter to product collection
      *
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
index b6eeea162c63d9efa13d09ce264fc5be75e234a8..8af927b7b893cd2d783ba0f4bc93b517cf460645 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
@@ -14,6 +14,7 @@ use Magento\CatalogInventory\Api\StockItemRepositoryInterface as StockItemReposi
 use Magento\CatalogInventory\Model\Indexer\Stock\Processor;
 use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as StockItemResource;
 use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
 use Magento\Framework\DB\MapperFactory;
 use Magento\Framework\DB\QueryBuilderFactory;
 use Magento\Framework\Exception\CouldNotDeleteException;
@@ -83,6 +84,11 @@ class StockItemRepository implements StockItemRepositoryInterface
      */
     protected $dateTime;
 
+    /**
+     * @var StockRegistryStorage
+     */
+    protected $stockRegistryStorage;
+
     /**
      * @param StockConfigurationInterface $stockConfiguration
      * @param StockStateProviderInterface $stockStateProvider
@@ -95,6 +101,7 @@ class StockItemRepository implements StockItemRepositoryInterface
      * @param TimezoneInterface $localeDate
      * @param Processor $indexProcessor
      * @param DateTime $dateTime
+     * @param StockRegistryStorage $stockRegistryStorage
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -108,7 +115,8 @@ class StockItemRepository implements StockItemRepositoryInterface
         MapperFactory $mapperFactory,
         TimezoneInterface $localeDate,
         Processor $indexProcessor,
-        DateTime $dateTime
+        DateTime $dateTime,
+        StockRegistryStorage $stockRegistryStorage
     ) {
         $this->stockConfiguration = $stockConfiguration;
         $this->stockStateProvider = $stockStateProvider;
@@ -118,10 +126,10 @@ class StockItemRepository implements StockItemRepositoryInterface
         $this->productFactory = $productFactory;
         $this->queryBuilderFactory = $queryBuilderFactory;
         $this->mapperFactory = $mapperFactory;
-        $this->mapperFactory = $mapperFactory;
         $this->localeDate = $localeDate;
         $this->indexProcessor = $indexProcessor;
         $this->dateTime = $dateTime;
+        $this->stockRegistryStorage = $stockRegistryStorage;
     }
 
     /**
@@ -163,7 +171,7 @@ class StockItemRepository implements StockItemRepositoryInterface
 
             $this->indexProcessor->reindexRow($stockItem->getProductId());
         } catch (\Exception $exception) {
-            throw new CouldNotSaveException(__($exception->getMessage()));
+            throw new CouldNotSaveException(__('Unable to save Stock Item'), $exception);
         }
         return $stockItem;
     }
@@ -201,8 +209,13 @@ class StockItemRepository implements StockItemRepositoryInterface
     {
         try {
             $this->resource->delete($stockItem);
+            $this->stockRegistryStorage->removeStockItem($stockItem->getProductId());
+            $this->stockRegistryStorage->removeStockStatus($stockItem->getProductId());
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock Item with id "%1"', $stockItem->getItemId()),
+                $exception
+            );
         }
         return true;
     }
@@ -216,7 +229,10 @@ class StockItemRepository implements StockItemRepositoryInterface
             $stockItem = $this->get($id);
             $this->delete($stockItem);
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock Item with id "%1"', $id),
+                $exception
+            );
         }
         return true;
     }
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php
index ceff54964d77e7211b4c9a89487e5bc61f354d6b..e76b239876ae6ba61ece6ff0d973af1b01687600 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php
@@ -10,6 +10,7 @@ use Magento\CatalogInventory\Api\Data\StockInterface;
 use Magento\CatalogInventory\Api\StockRepositoryInterface;
 use Magento\CatalogInventory\Model\ResourceModel\Stock as StockResource;
 use Magento\CatalogInventory\Model\StockFactory;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
 use Magento\Framework\DB\MapperFactory;
 use Magento\Framework\DB\QueryBuilderFactory;
 use Magento\Framework\Exception\CouldNotDeleteException;
@@ -47,25 +48,33 @@ class StockRepository implements StockRepositoryInterface
      */
     protected $mapperFactory;
 
+    /**
+     * @var StockRegistryStorage
+     */
+    protected $stockRegistryStorage;
+
     /**
      * @param StockResource $resource
      * @param StockFactory $stockFactory
      * @param StockCollectionInterfaceFactory $collectionFactory
      * @param QueryBuilderFactory $queryBuilderFactory
      * @param MapperFactory $mapperFactory
+     * @param StockRegistryStorage $stockRegistryStorage
      */
     public function __construct(
         StockResource $resource,
         StockFactory $stockFactory,
         StockCollectionInterfaceFactory $collectionFactory,
         QueryBuilderFactory $queryBuilderFactory,
-        MapperFactory $mapperFactory
+        MapperFactory $mapperFactory,
+        StockRegistryStorage $stockRegistryStorage
     ) {
         $this->resource = $resource;
         $this->stockFactory = $stockFactory;
         $this->stockCollectionFactory = $collectionFactory;
         $this->queryBuilderFactory = $queryBuilderFactory;
         $this->mapperFactory = $mapperFactory;
+        $this->stockRegistryStorage = $stockRegistryStorage;
     }
 
     /**
@@ -78,7 +87,7 @@ class StockRepository implements StockRepositoryInterface
         try {
             $this->resource->save($stock);
         } catch (\Exception $exception) {
-            throw new CouldNotSaveException(__($exception->getMessage()));
+            throw new CouldNotSaveException(__('Unable to save Stock'), $exception);
         }
         return $stock;
     }
@@ -121,8 +130,12 @@ class StockRepository implements StockRepositoryInterface
     {
         try {
             $this->resource->delete($stock);
+            $this->stockRegistryStorage->removeStock();
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock with id "%1"', $stock->getStockId()),
+                $exception
+            );
         }
         return true;
     }
@@ -138,7 +151,10 @@ class StockRepository implements StockRepositoryInterface
             $stock = $this->get($id);
             $this->delete($stock);
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock with id "%1"', $id),
+                $exception
+            );
         }
         return true;
     }
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php
index 04f845ed0cd2a2bcacbf1ea38309c4170639564a..3e20bbf5927eda85524dc7e3a33830d5de073da5 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php
@@ -9,6 +9,7 @@ use Magento\CatalogInventory\Api\Data\StockStatusCollectionInterfaceFactory;
 use Magento\CatalogInventory\Api\Data\StockStatusInterface;
 use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
 use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResource;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
 use Magento\Framework\DB\MapperFactory;
 use Magento\Framework\DB\QueryBuilderFactory;
 use Magento\Framework\Exception\CouldNotDeleteException;
@@ -45,25 +46,33 @@ class StockStatusRepository implements StockStatusRepositoryInterface
      */
     protected $mapperFactory;
 
+    /**
+     * @var StockRegistryStorage
+     */
+    protected $stockRegistryStorage;
+
     /**
      * @param StockStatusResource $resource
      * @param StatusFactory $stockStatusFactory
      * @param StockStatusCollectionInterfaceFactory $collectionFactory
      * @param QueryBuilderFactory $queryBuilderFactory
      * @param MapperFactory $mapperFactory
+     * @param StockRegistryStorage $stockRegistryStorage
      */
     public function __construct(
         StockStatusResource $resource,
         StatusFactory $stockStatusFactory,
         StockStatusCollectionInterfaceFactory $collectionFactory,
         QueryBuilderFactory $queryBuilderFactory,
-        MapperFactory $mapperFactory
+        MapperFactory $mapperFactory,
+        StockRegistryStorage $stockRegistryStorage
     ) {
         $this->resource = $resource;
         $this->stockStatusFactory = $stockStatusFactory;
         $this->stockStatusCollectionFactory = $collectionFactory;
         $this->queryBuilderFactory = $queryBuilderFactory;
         $this->mapperFactory = $mapperFactory;
+        $this->stockRegistryStorage = $stockRegistryStorage;
     }
 
     /**
@@ -76,7 +85,7 @@ class StockStatusRepository implements StockStatusRepositoryInterface
         try {
             $this->resource->save($stockStatus);
         } catch (\Exception $exception) {
-            throw new CouldNotSaveException(__($exception->getMessage()));
+            throw new CouldNotSaveException(__('Unable to save Stock Status'), $exception);
         }
         return $stockStatus;
     }
@@ -115,8 +124,12 @@ class StockStatusRepository implements StockStatusRepositoryInterface
     {
         try {
             $this->resource->delete($stockStatus);
+            $this->stockRegistryStorage->removeStockStatus($stockStatus->getProductId());
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock Status for product %1', $stockStatus->getProductId()),
+                $exception
+            );
         }
         return true;
     }
@@ -132,7 +145,10 @@ class StockStatusRepository implements StockStatusRepositoryInterface
             $stockStatus = $this->get($id);
             $this->delete($stockStatus);
         } catch (\Exception $exception) {
-            throw new CouldNotDeleteException(__($exception->getMessage()));
+            throw new CouldNotDeleteException(
+                __('Unable to remove Stock Status for product %1', $id),
+                $exception
+            );
         }
         return true;
     }
diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php b/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
index e8f7ae5cc723fb8219aaf9990c1d23017cb35b4e..87e91c863220b4cd4be87f16406d54cd01691497 100644
--- a/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
+++ b/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
@@ -69,19 +69,9 @@ class StockRegistryProvider implements StockRegistryProviderInterface
     protected $stockStatusCriteriaFactory;
 
     /**
-     * @var array
+     * @var StockRegistryStorage
      */
-    protected $stocks = [];
-
-    /**
-     * @var array
-     */
-    protected $stockItems = [];
-
-    /**
-     * @var array
-     */
-    protected $stockStatuses = [];
+    protected $stockRegistryStorage;
 
     /**
      * @param StockRepositoryInterface $stockRepository
@@ -93,6 +83,8 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      * @param StockCriteriaInterfaceFactory $stockCriteriaFactory
      * @param StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory
      * @param StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
+     * @param StockRegistryStorage $stockRegistryStorage
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
         StockRepositoryInterface $stockRepository,
@@ -103,7 +95,8 @@ class StockRegistryProvider implements StockRegistryProviderInterface
         StockStatusInterfaceFactory $stockStatusFactory,
         StockCriteriaInterfaceFactory $stockCriteriaFactory,
         StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory,
-        StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
+        StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory,
+        StockRegistryStorage $stockRegistryStorage
     ) {
         $this->stockRepository = $stockRepository;
         $this->stockFactory = $stockFactory;
@@ -111,10 +104,10 @@ class StockRegistryProvider implements StockRegistryProviderInterface
         $this->stockItemFactory = $stockItemFactory;
         $this->stockStatusRepository = $stockStatusRepository;
         $this->stockStatusFactory = $stockStatusFactory;
-
         $this->stockCriteriaFactory = $stockCriteriaFactory;
         $this->stockItemCriteriaFactory = $stockItemCriteriaFactory;
         $this->stockStatusCriteriaFactory = $stockStatusCriteriaFactory;
+        $this->stockRegistryStorage = $stockRegistryStorage;
     }
 
     /**
@@ -123,18 +116,19 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      */
     public function getStock($scopeId)
     {
-        if (!isset($this->stocks[$scopeId])) {
+        $stock = $this->stockRegistryStorage->getStock($scopeId);
+        if (null === $stock) {
             $criteria = $this->stockCriteriaFactory->create();
             $criteria->setScopeFilter($scopeId);
             $collection = $this->stockRepository->getList($criteria);
             $stock = current($collection->getItems());
             if ($stock && $stock->getStockId()) {
-                $this->stocks[$scopeId] = $stock;
+                $this->stockRegistryStorage->setStock($scopeId, $stock);
             } else {
-                return $this->stockFactory->create();
+                $stock = $this->stockFactory->create();
             }
         }
-        return $this->stocks[$scopeId];
+        return $stock;
     }
 
     /**
@@ -144,19 +138,19 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      */
     public function getStockItem($productId, $scopeId)
     {
-        $key = $scopeId . '/' . $productId;
-        if (!isset($this->stockItems[$key])) {
+        $stockItem = $this->stockRegistryStorage->getStockItem($productId, $scopeId);
+        if (null === $stockItem) {
             $criteria = $this->stockItemCriteriaFactory->create();
             $criteria->setProductsFilter($productId);
             $collection = $this->stockItemRepository->getList($criteria);
             $stockItem = current($collection->getItems());
             if ($stockItem && $stockItem->getItemId()) {
-                $this->stockItems[$key] = $stockItem;
+                $this->stockRegistryStorage->setStockItem($productId, $scopeId, $stockItem);
             } else {
-                return $this->stockItemFactory->create();
+                $stockItem = $this->stockItemFactory->create();
             }
         }
-        return $this->stockItems[$key];
+        return $stockItem;
     }
 
     /**
@@ -166,19 +160,19 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      */
     public function getStockStatus($productId, $scopeId)
     {
-        $key = $scopeId . '/' . $productId;
-        if (!isset($this->stockStatuses[$key])) {
+        $stockStatus = $this->stockRegistryStorage->getStockStatus($productId, $scopeId);
+        if (null === $stockStatus) {
             $criteria = $this->stockStatusCriteriaFactory->create();
             $criteria->setProductsFilter($productId);
             $criteria->setScopeFilter($scopeId);
             $collection = $this->stockStatusRepository->getList($criteria);
             $stockStatus = current($collection->getItems());
             if ($stockStatus && $stockStatus->getProductId()) {
-                $this->stockStatuses[$key] = $stockStatus;
+                $this->stockRegistryStorage->setStockStatus($productId, $scopeId, $stockStatus);
             } else {
-                return $this->stockStatusFactory->create();
+                $stockStatus = $this->stockStatusFactory->create();
             }
         }
-        return $this->stockStatuses[$key];
+        return $stockStatus;
     }
 }
diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistryStorage.php b/app/code/Magento/CatalogInventory/Model/StockRegistryStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7c29e7fc9aa163d0ea8142348d7a28d0a37b310
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/StockRegistryStorage.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogInventory\Model;
+
+use Magento\CatalogInventory\Api\Data\StockInterface;
+use Magento\CatalogInventory\Api\Data\StockItemInterface;
+use Magento\CatalogInventory\Api\Data\StockStatusInterface;
+
+/**
+ * Class StockRegistryStorage
+ */
+class StockRegistryStorage
+{
+    /**
+     * @var array
+     */
+    protected $stocks = [];
+
+    /**
+     * @var array
+     */
+    private $stockItems = [];
+
+    /**
+     * @var array
+     */
+    private $stockStatuses = [];
+
+    /**
+     * @param int $scopeId
+     * @return StockInterface
+     */
+    public function getStock($scopeId)
+    {
+        return isset($this->stocks[$scopeId]) ? $this->stocks[$scopeId] : null;
+    }
+
+    /**
+     * @param int $scopeId
+     * @param StockInterface $value
+     * @return void
+     */
+    public function setStock($scopeId, StockInterface $value)
+    {
+        $this->stocks[$scopeId] = $value;
+    }
+
+    /**
+     * @param int|null $scopeId
+     * @return void
+     */
+    public function removeStock($scopeId = null)
+    {
+        if (null === $scopeId) {
+            $this->stocks = [];
+        } else {
+            unset($this->stocks[$scopeId]);
+        }
+    }
+
+    /**
+     * @param int $productId
+     * @param int $scopeId
+     * @return StockItemInterface
+     */
+    public function getStockItem($productId, $scopeId)
+    {
+        return isset($this->stockItems[$productId][$scopeId]) ? $this->stockItems[$productId][$scopeId] : null;
+    }
+
+    /**
+     * @param int $productId
+     * @param int $scopeId
+     * @param StockItemInterface $value
+     * @return void
+     */
+    public function setStockItem($productId, $scopeId, StockItemInterface $value)
+    {
+        $this->stockItems[$productId][$scopeId] = $value;
+    }
+
+    /**
+     * @param int $productId
+     * @param int|null $scopeId
+     * @return void
+     */
+    public function removeStockItem($productId, $scopeId = null)
+    {
+        if (null === $scopeId) {
+            unset($this->stockItems[$productId]);
+        } else {
+            unset($this->stockItems[$productId][$scopeId]);
+        }
+    }
+
+    /**
+     * @param int $productId
+     * @param int $scopeId
+     * @return StockStatusInterface
+     */
+    public function getStockStatus($productId, $scopeId)
+    {
+        return isset($this->stockStatuses[$productId][$scopeId]) ? $this->stockStatuses[$productId][$scopeId] : null;
+    }
+
+    /**
+     * @param int $productId
+     * @param int $scopeId
+     * @param StockStatusInterface $value
+     * @return void
+     */
+    public function setStockStatus($productId, $scopeId, StockStatusInterface $value)
+    {
+        $this->stockStatuses[$productId][$scopeId] = $value;
+    }
+
+    /**
+     * @param int $productId
+     * @param int|null $scopeId
+     * @return void
+     */
+    public function removeStockStatus($productId, $scopeId = null)
+    {
+        if (null === $scopeId) {
+            unset($this->stockStatuses[$productId]);
+        } else {
+            unset($this->stockStatuses[$productId][$scopeId]);
+        }
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/StockState.php b/app/code/Magento/CatalogInventory/Model/StockState.php
index c3d0dc9aef0fd317d0d69e8b1b06b01b5af4f7e7..96b3162966211b4c21d0e206a12246b91b092777 100644
--- a/app/code/Magento/CatalogInventory/Model/StockState.php
+++ b/app/code/Magento/CatalogInventory/Model/StockState.php
@@ -146,7 +146,7 @@ class StockState implements StockStateInterface
      * @param float $qtyToCheck
      * @param float $origQty
      * @param int $scopeId
-     * @return \Magento\Framework\DataObject
+     * @return int
      */
     public function checkQuoteItemQty($productId, $itemQty, $qtyToCheck, $origQty, $scopeId = null)
     {
diff --git a/app/code/Magento/CatalogInventory/Observer/AddStockStatusToCollectionObserver.php b/app/code/Magento/CatalogInventory/Observer/AddStockStatusToCollectionObserver.php
deleted file mode 100644
index e777f81ba68425cce30934f2852771aac7acfec5..0000000000000000000000000000000000000000
--- a/app/code/Magento/CatalogInventory/Observer/AddStockStatusToCollectionObserver.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\CatalogInventory\Observer;
-
-use Magento\Framework\Event\ObserverInterface;
-use Magento\Framework\Event\Observer as EventObserver;
-
-/**
- * Catalog inventory module observer
- */
-class AddStockStatusToCollectionObserver implements ObserverInterface
-{
-    /**
-     * @var \Magento\CatalogInventory\Helper\Stock
-     */
-    protected $stockHelper;
-
-    /**
-     * @param \Magento\CatalogInventory\Helper\Stock $stockHelper
-     */
-    public function __construct(\Magento\CatalogInventory\Helper\Stock $stockHelper)
-    {
-        $this->stockHelper = $stockHelper;
-    }
-
-    /**
-     * Add information about product stock status to collection
-     * Used in for product collection after load
-     *
-     * @param EventObserver $observer
-     * @return void
-     */
-    public function execute(EventObserver $observer)
-    {
-        $productCollection = $observer->getEvent()->getCollection();
-        $this->stockHelper->addStockStatusToProducts($productCollection);
-    }
-}
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Block/Stockqty/DefaultStockqtyTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Block/Stockqty/DefaultStockqtyTest.php
index fd247ef871523bf14f3ee2480af22f3c648d6627..5c9396d32bfb526d7bd3da92856c00513f3c01ba 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Block/Stockqty/DefaultStockqtyTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Block/Stockqty/DefaultStockqtyTest.php
@@ -20,11 +20,6 @@ class DefaultStockqtyTest extends \PHPUnit_Framework_TestCase
      */
     protected $registryMock;
 
-    /**
-     * @var \Magento\CatalogInventory\Api\StockStateInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockState;
-
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -39,13 +34,6 @@ class DefaultStockqtyTest extends \PHPUnit_Framework_TestCase
     {
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
         $this->registryMock = $this->getMock('Magento\Framework\Registry', [], [], '', false);
-        $this->stockState = $this->getMock(
-            'Magento\CatalogInventory\Api\StockStateInterface',
-            [],
-            [],
-            '',
-            false
-        );
         $this->stockRegistryMock = $this->getMockBuilder('Magento\CatalogInventory\Api\StockRegistryInterface')
             ->disableOriginalConstructor()
             ->getMock();
@@ -56,7 +44,6 @@ class DefaultStockqtyTest extends \PHPUnit_Framework_TestCase
             'Magento\CatalogInventory\Block\Stockqty\DefaultStockqty',
             [
                 'registry' => $this->registryMock,
-                'stockState' => $this->stockState,
                 'stockRegistry' => $this->stockRegistryMock,
                 'scopeConfig' => $this->scopeConfigMock
             ]
@@ -112,10 +99,13 @@ class DefaultStockqtyTest extends \PHPUnit_Framework_TestCase
                 ->will($this->returnValue($product));
 
             if ($productId) {
-                $this->stockState->expects($this->once())
-                    ->method('getStockQty')
+                $stockStatus = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockStatusInterface::class)
+                    ->getMockForAbstractClass();
+                $stockStatus->expects($this->any())->method('getQty')->willReturn($productStockQty);
+                $this->stockRegistryMock->expects($this->once())
+                    ->method('getStockStatus')
                     ->with($this->equalTo($productId), $this->equalTo($websiteId))
-                    ->will($this->returnValue($productStockQty));
+                    ->will($this->returnValue($stockStatus));
             }
         }
         $this->assertSame($expectedQty, $this->block->getStockQty());
@@ -157,10 +147,6 @@ class DefaultStockqtyTest extends \PHPUnit_Framework_TestCase
             ->method('getStockItem')
             ->with($productId)
             ->willReturn($stockItemMock);
-        $this->stockState->expects($this->once())
-            ->method('getStockQty')
-            ->with($productId, $storeMock)
-            ->willReturn($stockQty);
 
         $this->assertEquals($stockQty, $this->block->getStockQtyLeft());
     }
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
index cb4209de616e798498590461c9db2e3e1891657d..01356277c5acf1f1a99f8ddfd374d85daabe6332 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
@@ -182,10 +182,10 @@ class StockTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $stockStatusMock = $this->getMockBuilder('Magento\CatalogInventory\Model\ResourceModel\Stock\Status')
             ->disableOriginalConstructor()
-            ->setMethods(['addIsInStockFilterToCollection'])
+            ->setMethods(['addStockDataToCollection'])
             ->getMock();
         $stockStatusMock->expects($this->once())
-            ->method('addIsInStockFilterToCollection')
+            ->method('addStockDataToCollection')
             ->with($collectionMock);
         $this->statusFactoryMock->expects($this->once())
             ->method('create')
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/AddStockStatusToCollectionTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/AddStockStatusToCollectionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..9d1796cbe117e781d7a88ca76cc4dd058fcece8b
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/AddStockStatusToCollectionTest.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogInventory\Test\Unit\Model;
+
+use Magento\CatalogInventory\Model\AddStockStatusToCollection;
+
+class AddStockStatusToCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var AddStockStatusToCollection
+     */
+    protected $plugin;
+
+    /**
+     * @var \Magento\CatalogInventory\Helper\Stock|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockHelper;
+
+    protected function setUp()
+    {
+        $this->stockHelper = $this->getMock('Magento\CatalogInventory\Helper\Stock', [], [], '', false);
+        $this->plugin = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
+            'Magento\CatalogInventory\Model\AddStockStatusToCollection',
+            [
+                'stockHelper' => $this->stockHelper,
+            ]
+        );
+    }
+
+    public function testAddStockStatusToCollection()
+    {
+        $productCollection = $this->getMockBuilder('Magento\Catalog\Model\ResourceModel\Product\Collection')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->stockHelper->expects($this->once())
+            ->method('addIsInStockFilterToCollection')
+            ->with($productCollection)
+            ->will($this->returnSelf());
+
+        $this->plugin->beforeLoad($productCollection);
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
index a2db32bdb642a6c50223fe5812047431f2861fb3..76c6e6cf923c563d158d3b72503333f9b35af07b 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
@@ -5,8 +5,10 @@
  */
 namespace Magento\CatalogInventory\Test\Unit\Model\Stock;
 
-use \Magento\CatalogInventory\Model\Stock\StockItemRepository;
-use \Magento\CatalogInventory\Api\Data as InventoryApiData;
+use Magento\CatalogInventory\Model\Stock\StockItemRepository;
+use Magento\CatalogInventory\Api\Data as InventoryApiData;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 /**
  * Class StockItemRepositoryTest
@@ -24,6 +26,7 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
      * @var \Magento\CatalogInventory\Model\Stock\Item |\PHPUnit_Framework_MockObject_MockObject
      */
     protected $stockItemMock;
+
     /**
      * @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -84,6 +87,14 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $dateTime;
 
+    /**
+     * @var StockRegistryStorage|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockRegistryStorage;
+
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
     protected function setUp()
     {
         $this->stockItemMock = $this->getMockBuilder('\Magento\CatalogInventory\Model\Stock\Item')
@@ -168,24 +179,36 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $this->stockRegistryStorage = $this->getMockBuilder(StockRegistryStorage::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $this->model = new StockItemRepository(
-            $this->stockConfigurationMock,
-            $this->stockStateProviderMock,
-            $this->stockItemResourceMock,
-            $this->stockItemFactoryMock,
-            $this->stockItemCollectionMock,
-            $this->productFactoryMock,
-            $this->queryBuilderFactoryMock,
-            $this->mapperMock,
-            $this->localeDateMock,
-            $this->indexProcessorMock,
-            $this->dateTime
+        $this->model = (new ObjectManager($this))->getObject(
+            StockItemRepository::class,
+            [
+                'stockConfiguration' => $this->stockConfigurationMock,
+                'stockStateProvider' => $this->stockStateProviderMock,
+                'resource' => $this->stockItemResourceMock,
+                'stockItemFactory' => $this->stockItemFactoryMock,
+                'stockItemCollectionFactory' => $this->stockItemCollectionMock,
+                'productFactory' => $this->productFactoryMock,
+                'queryBuilderFactory' => $this->queryBuilderFactoryMock,
+                'mapperFactory' => $this->mapperMock,
+                'localeDate' => $this->localeDateMock,
+                'indexProcessor' => $this->indexProcessorMock,
+                'dateTime' => $this->dateTime,
+                'stockRegistryStorage' => $this->stockRegistryStorage,
+            ]
         );
     }
 
     public function testDelete()
     {
+        $productId = 1;
+        $this->stockItemMock->expects($this->atLeastOnce())->method('getProductId')->willReturn($productId);
+        $this->stockRegistryStorage->expects($this->once())->method('removeStockItem')->with($productId);
+        $this->stockRegistryStorage->expects($this->once())->method('removeStockStatus')->with($productId);
+
         $this->stockItemResourceMock->expects($this->once())
             ->method('delete')
             ->with($this->stockItemMock)
@@ -220,7 +243,7 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \Magento\Framework\Exception\CouldNotDeleteException
-     * @expectedExceptionMessage Stock Item with id "1" does not exist.
+     * @expectedExceptionMessage Unable to remove Stock Item with id "1"
      */
     public function testDeleteByIdException()
     {
@@ -286,6 +309,8 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId);
         $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf();
         $this->productMock->expects($this->once())->method('getId')->willReturn(null);
+        $this->stockRegistryStorage->expects($this->never())->method('removeStockItem');
+        $this->stockRegistryStorage->expects($this->never())->method('removeStockStatus');
 
         $this->assertEquals($this->stockItemMock, $this->model->save($this->stockItemMock));
     }
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockRepositoryTest.php
index 3e9394a3995e53102bae88c0496080f15af113c3..779958ccf4c20fc650489926e4a907ac4d630bca 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockRepositoryTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockRepositoryTest.php
@@ -5,7 +5,9 @@
  */
 namespace Magento\CatalogInventory\Test\Unit\Model\Stock;
 
-use \Magento\CatalogInventory\Model\Stock\StockRepository;
+use Magento\CatalogInventory\Model\Stock\StockRepository;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 /**
  * Class StockRepositoryTest
@@ -47,9 +49,13 @@ class StockRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $mapperMock;
 
+    /**
+     * @var StockRegistryStorage|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockRegistryStorage;
+
     protected function setUp()
     {
-
         $this->stockMock = $this->getMockBuilder('\Magento\CatalogInventory\Model\Stock')
             ->disableOriginalConstructor()
             ->getMock();
@@ -77,13 +83,20 @@ class StockRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->mapperMock = $this->getMockBuilder('Magento\Framework\DB\MapperFactory')
             ->disableOriginalConstructor()
             ->getMock();
+        $this->stockRegistryStorage = $this->getMockBuilder(StockRegistryStorage::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $this->model = new StockRepository(
-            $this->stockResourceMock,
-            $this->stockFactoryMock,
-            $this->stockCollectionMock,
-            $this->queryBuilderFactoryMock,
-            $this->mapperMock
+        $this->model = (new ObjectManager($this))->getObject(
+            StockRepository::class,
+            [
+                'resource' => $this->stockResourceMock,
+                'stockFactory' => $this->stockFactoryMock,
+                'collectionFactory' => $this->stockCollectionMock,
+                'queryBuilderFactory' => $this->queryBuilderFactoryMock,
+                'mapperFactory' => $this->mapperMock,
+                'stockRegistryStorage' => $this->stockRegistryStorage,
+            ]
         );
     }
 
@@ -137,6 +150,8 @@ class StockRepositoryTest extends \PHPUnit_Framework_TestCase
 
     public function testDelete()
     {
+        $this->stockRegistryStorage->expects($this->once())->method('removeStock');
+
         $this->stockResourceMock->expects($this->once())
             ->method('delete')
             ->with($this->stockMock)
@@ -171,7 +186,7 @@ class StockRepositoryTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \Magento\Framework\Exception\CouldNotDeleteException
-     * @expectedExceptionMessage Stock with id "1" does not exist.
+     * @expectedExceptionMessage Unable to remove Stock with id "1"
      */
     public function testDeleteByIdException()
     {
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockStatusRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockStatusRepositoryTest.php
index 5c8d3efe356db03d40ec8ba1d4b43c0b32e336b2..5feb8e85af5421ab320fb77f9938c2458bffca80 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockStatusRepositoryTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockStatusRepositoryTest.php
@@ -5,8 +5,10 @@
  */
 namespace Magento\CatalogInventory\Test\Unit\Model\Stock;
 
-use \Magento\CatalogInventory\Model\Stock\StockStatusRepository;
-use \Magento\CatalogInventory\Api\Data as InventoryApiData;
+use Magento\CatalogInventory\Model\Stock\StockStatusRepository;
+use Magento\CatalogInventory\Api\Data as InventoryApiData;
+use Magento\CatalogInventory\Model\StockRegistryStorage;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 /**
  * Class StockStatusRepositoryTest
@@ -48,6 +50,11 @@ class StockStatusRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $mapperMock;
 
+    /**
+     * @var StockRegistryStorage|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockRegistryStorage;
+
     protected function setUp()
     {
         $this->stockStatusMock = $this->getMockBuilder('\Magento\CatalogInventory\Model\Stock\Status')
@@ -76,13 +83,20 @@ class StockStatusRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->mapperMock = $this->getMockBuilder('Magento\Framework\DB\MapperFactory')
             ->disableOriginalConstructor()
             ->getMock();
+        $this->stockRegistryStorage = $this->getMockBuilder(StockRegistryStorage::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $this->model = new StockStatusRepository(
-            $this->stockStatusResourceMock,
-            $this->stockStatusFactoryMock,
-            $this->stockStatusCollectionMock,
-            $this->queryBuilderFactoryMock,
-            $this->mapperMock
+        $this->model = (new ObjectManager($this))->getObject(
+            StockStatusRepository::class,
+            [
+                'resource' => $this->stockStatusResourceMock,
+                'stockStatusFactory' => $this->stockStatusFactoryMock,
+                'collectionFactory' => $this->stockStatusCollectionMock,
+                'queryBuilderFactory' => $this->queryBuilderFactoryMock,
+                'mapperFactory' => $this->mapperMock,
+                'stockRegistryStorage' => $this->stockRegistryStorage,
+            ]
         );
     }
 
@@ -136,6 +150,10 @@ class StockStatusRepositoryTest extends \PHPUnit_Framework_TestCase
 
     public function testDelete()
     {
+        $productId = 1;
+        $this->stockStatusMock->expects($this->atLeastOnce())->method('getProductId')->willReturn($productId);
+        $this->stockRegistryStorage->expects($this->once())->method('removeStockStatus')->with($productId);
+
         $this->stockStatusResourceMock->expects($this->once())
             ->method('delete')
             ->with($this->stockStatusMock)
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockStatusToCollectionObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockStatusToCollectionObserverTest.php
deleted file mode 100644
index ebba8ffe469324f70f4871d73cc438690b13a911..0000000000000000000000000000000000000000
--- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockStatusToCollectionObserverTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\CatalogInventory\Test\Unit\Observer;
-
-use Magento\CatalogInventory\Observer\AddStockStatusToCollectionObserver;
-
-class AddStockStatusToCollectionObserverTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var AddStockStatusToCollectionObserver
-     */
-    protected $observer;
-
-    /**
-     * @var \Magento\CatalogInventory\Helper\Stock|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockHelper;
-
-    /**
-     * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $event;
-
-    /**
-     * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $eventObserver;
-
-    protected function setUp()
-    {
-        $this->stockHelper = $this->getMock('Magento\CatalogInventory\Helper\Stock', [], [], '', false);
-
-        $this->event = $this->getMockBuilder('Magento\Framework\Event')
-            ->disableOriginalConstructor()
-            ->setMethods(['getCollection'])
-            ->getMock();
-
-        $this->eventObserver = $this->getMockBuilder('Magento\Framework\Event\Observer')
-            ->disableOriginalConstructor()
-            ->setMethods(['getEvent'])
-            ->getMock();
-
-        $this->eventObserver->expects($this->atLeastOnce())
-            ->method('getEvent')
-            ->will($this->returnValue($this->event));
-
-        $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
-            'Magento\CatalogInventory\Observer\AddStockStatusToCollectionObserver',
-            [
-                'stockHelper' => $this->stockHelper,
-            ]
-        );
-    }
-
-    public function testAddStockStatusToCollection()
-    {
-        $productCollection = $this->getMockBuilder('Magento\Catalog\Model\ResourceModel\Product\Collection')
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $this->event->expects($this->once())
-            ->method('getCollection')
-            ->will($this->returnValue($productCollection));
-
-        $this->stockHelper->expects($this->once())
-            ->method('addStockStatusToProducts')
-            ->with($productCollection)
-            ->will($this->returnSelf());
-
-        $this->observer->execute($this->eventObserver);
-    }
-}
diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml
index 4252a093c4fe710f7a8a0cb64abb5777f0b5ef1a..96b08cf5123fbc58c53dfd42de556fd5eb1a2f21 100644
--- a/app/code/Magento/CatalogInventory/etc/events.xml
+++ b/app/code/Magento/CatalogInventory/etc/events.xml
@@ -12,12 +12,6 @@
     <event name="catalog_product_load_after">
         <observer name="inventory" instance="Magento\CatalogInventory\Observer\AddInventoryDataObserver"/>
     </event>
-    <event name="catalog_product_collection_load_after">
-        <observer name="inventory" instance="Magento\CatalogInventory\Observer\AddStockStatusToCollectionObserver"/>
-    </event>
-    <event name="sales_quote_item_collection_products_after_load">
-        <observer name="inventory" instance="Magento\CatalogInventory\Observer\AddStockStatusToCollectionObserver"/>
-    </event>
     <event name="sales_quote_item_qty_set_after">
         <observer name="inventory" instance="Magento\CatalogInventory\Observer\QuantityValidatorObserver"/>
     </event>
diff --git a/app/code/Magento/CatalogInventory/etc/frontend/di.xml b/app/code/Magento/CatalogInventory/etc/frontend/di.xml
index baa96cc29bb52cc66798ea39b2fe4a75397eef87..5d5fa9aab000f32f7eca3253bfc176f4e9f6e64a 100644
--- a/app/code/Magento/CatalogInventory/etc/frontend/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/frontend/di.xml
@@ -9,4 +9,7 @@
     <type name="Magento\Catalog\Model\Product\Link">
         <plugin name="isInStockFilter" type="Magento\CatalogInventory\Model\Plugin\ProductLinks" />
     </type>
+    <type name="Magento\Catalog\Model\ResourceModel\Product\Collection">
+        <plugin name="add_stock_information" type="Magento\CatalogInventory\Model\AddStockStatusToCollection" />
+    </type>
 </config>
diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php
index 38c87413b2b410f375a4c58156e4fbe143f27b35..da07e516427d1c96b4e1aa29cda43e28f7e47b52 100644
--- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php
+++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php
@@ -75,8 +75,7 @@ class DataProvider
             $derivedTable->from(
                 ['main_table' => $this->resource->getTableName('catalog_category_product_index')],
                 [
-                    'entity_id' => 'product_id',
-                    'value' => 'category_id',
+                    'value' => 'category_id'
                 ]
             )->where('main_table.store_id = ?', $currentScopeId);
             $derivedTable->joinInner(
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
index b326b2a59d72db1797d4e5ca36a39012bf85b616..01c123190d2e224f819fb8423084646b53e3d638 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
@@ -92,17 +92,24 @@ class Attribute extends AbstractFilter
             if (empty($option['value'])) {
                 continue;
             }
+
+            $value = $option['value'];
+
+            $count = isset($optionsFacetedData[$value]['count'])
+                ? (int)$optionsFacetedData[$value]['count']
+                : 0;
             // Check filter type
-            if (empty($optionsFacetedData[$option['value']]['count'])
-                || ($this->getAttributeIsFilterable($attribute) == static::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS
-                    && !$this->isOptionReducesResults($optionsFacetedData[$option['value']]['count'], $productSize))
+            if (
+                $count === 0
+                && $this->getAttributeIsFilterable($attribute) === static::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS
+                && !$this->isOptionReducesResults($count, $productSize)
             ) {
                 continue;
             }
             $this->itemDataBuilder->addItemData(
                 $this->tagFilter->filter($option['label']),
-                $option['value'],
-                $optionsFacetedData[$option['value']]['count']
+                $value,
+                $count
             );
         }
 
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
index 77d097019a37a585a248c65c5cee8910c9571e30..35b502da8be49b450b0587b73492084f0d7d164b 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
@@ -283,15 +283,6 @@ class AttributeTest extends \PHPUnit_Framework_TestCase
         $this->attribute->expects($this->exactly(2))
             ->method('getAttributeCode')
             ->will($this->returnValue($attributeCode));
-        $this->attribute->expects($this->at(3))
-            ->method('getIsFilterable')
-            ->will($this->returnValue(1));
-        $this->attribute->expects($this->at(4))
-            ->method('getIsFilterable')
-            ->will($this->returnValue(2));
-        $this->attribute->expects($this->at(5))
-            ->method('getIsFilterable')
-            ->will($this->returnValue(1));
 
         $this->target->setAttributeModel($this->attribute);
 
diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php
index 018fed2f513acd285b86c0bc6a13973144bd6cf5..30506d354281c5de5bdc80422f4e1d1be51dbb06 100644
--- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php
+++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php
@@ -24,13 +24,11 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor
      */
     protected $cartManagement;
 
-
     /**
      * @var PaymentDetailsFactory
      */
     protected $paymentDetailsFactory;
 
-
     /**
      * @var \Magento\Quote\Api\CartTotalRepositoryInterface
      */
diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field.php b/app/code/Magento/Config/Block/System/Config/Form/Field.php
index a138be8accda554d263fb1e1f6f01d9bf6159e7f..b2bb1f1103af70af73311f0a369377d25be5c504 100644
--- a/app/code/Magento/Config/Block/System/Config/Form/Field.php
+++ b/app/code/Magento/Config/Block/System/Config/Form/Field.php
@@ -180,7 +180,7 @@ class Field extends \Magento\Backend\Block\Template implements \Magento\Framewor
      * @param string $html
      * @return string
      */
-    protected function _decorateRowHtml($element, $html)
+    protected function _decorateRowHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element, $html)
     {
         return '<tr id="row_' . $element->getHtmlId() . '">' . $html . '</tr>';
     }
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/NotificationTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/NotificationTest.php
index a86323a4d9880b9071edc24f456b4ffd3268228a..ffb8c3bb285788cd8cbf419c31ee545598bbe245 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/NotificationTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/NotificationTest.php
@@ -20,6 +20,13 @@ class NotificationTest extends \PHPUnit_Framework_TestCase
 
         /** @var \Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface $dateTimeFormatter */
         $dateTimeFormatter = $objectManager->getObject('Magento\Framework\Stdlib\DateTime\DateTimeFormatter');
+        $localeResolver = $objectManager->getObject('Magento\Framework\Locale\Resolver');
+
+        $reflection = new \ReflectionClass('Magento\Framework\Stdlib\DateTime\DateTimeFormatter');
+        $reflectionProperty = $reflection->getProperty('localeResolver');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($dateTimeFormatter, $localeResolver);
+
         $formattedDate = $dateTimeFormatter->formatObject($testDatetime);
 
         $htmlId = 'test_HTML_id';
diff --git a/app/code/Magento/Config/etc/system.xsd b/app/code/Magento/Config/etc/system.xsd
index 62ab3f085391d0f7c64c922655bd2aa15634775c..ecede8b085fa9973cb48a4a39ad9e0560de41074 100644
--- a/app/code/Magento/Config/etc/system.xsd
+++ b/app/code/Magento/Config/etc/system.xsd
@@ -95,6 +95,7 @@
                     <xs:element name="header_css" type="xs:string" />
                     <xs:element name="resource" type="typeAclResourceId" />
                     <xs:element ref="group" />
+                    <xs:element name="frontend_model" type="typeModel" />
                 </xs:choice>
             </xs:sequence>
             <xs:attributeGroup ref="elementsAttributeGroup"/>
diff --git a/app/code/Magento/Config/etc/system_file.xsd b/app/code/Magento/Config/etc/system_file.xsd
index 7d134b518b12018112cc76775e973c1da1a32ffb..514e287e6b0014853590e4ca97398aa2dfb99dd3 100644
--- a/app/code/Magento/Config/etc/system_file.xsd
+++ b/app/code/Magento/Config/etc/system_file.xsd
@@ -106,6 +106,7 @@
                     <xs:element name="resource" type="typeAclResourceId" />
                     <xs:element ref="group" />
                     <xs:element name="include" type="includeType"/>
+                    <xs:element name="frontend_model" type="typeModel" />
                 </xs:choice>
             </xs:sequence>
 
diff --git a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php
index e71e7ccf2f377261b46efc22b974db0d25d22d39..8fe421128e2e437664276eaad91d6b7dcf61dec0 100644
--- a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php
+++ b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php
@@ -9,6 +9,7 @@ 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;
+use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
 
 /**
  * Class Loader
@@ -20,14 +21,23 @@ class Loader
      */
     private $optionValueFactory;
 
+    /**
+     * @var JoinProcessorInterface
+     */
+    private $extensionAttributesJoinProcessor;
+
     /**
      * ReadHandler constructor
      *
      * @param OptionValueInterfaceFactory $optionValueFactory
+     * @param JoinProcessorInterface $extensionAttributesJoinProcessor
      */
-    public function __construct(OptionValueInterfaceFactory $optionValueFactory)
-    {
+    public function __construct(
+        OptionValueInterfaceFactory $optionValueFactory,
+        JoinProcessorInterface $extensionAttributesJoinProcessor
+    ) {
         $this->optionValueFactory = $optionValueFactory;
+        $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
     }
 
     /**
@@ -39,8 +49,8 @@ class Loader
         $options = [];
         /** @var Configurable $typeInstance */
         $typeInstance = $product->getTypeInstance();
-        $attributeCollection = $typeInstance->getConfigurableAttributes($product);
-
+        $attributeCollection = $typeInstance->getConfigurableAttributeCollection($product);
+        $this->extensionAttributesJoinProcessor->process($attributeCollection);
         foreach ($attributeCollection as $attribute) {
             $values = [];
             $attributeOptions = $attribute->getOptions();
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index 727b090c73e5b1139436a539943954578ed22f35..b7c4a060bf7e4e685bec38bebe36d657662f130c 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -134,6 +134,11 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
      */
     protected $extensionAttributesJoinProcessor;
 
+    /**
+     * @var \Magento\Framework\Cache\FrontendInterface
+     */
+    private $cache;
+
     /**
      * @var MetadataPool
      */
@@ -159,7 +164,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(
@@ -180,6 +185,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
         \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
         \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor,
+        \Magento\Framework\Cache\FrontendInterface $cache,
         MetadataPool $metadataPool
     ) {
         $this->typeConfigurableFactory = $typeConfigurableFactory;
@@ -191,7 +197,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
         $this->_scopeConfig = $scopeConfig;
         $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
         $this->metadataPool = $metadataPool;
-
+        $this->cache = $cache;
         parent::__construct(
             $catalogProductOption,
             $eavConfig,
@@ -203,6 +209,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
             $logger,
             $productRepository
         );
+
     }
 
     /**
@@ -364,9 +371,21 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
             ['group' => 'CONFIGURABLE', 'method' => __METHOD__]
         );
         if (!$product->hasData($this->_configurableAttributes)) {
-            $configurableAttributes = $this->getConfigurableAttributeCollection($product);
-            $this->extensionAttributesJoinProcessor->process($configurableAttributes);
-            $configurableAttributes->orderByPosition()->load();
+            $cacheId =  __CLASS__ . $product->getId();
+            $configurableAttributes = $this->cache->load($cacheId);
+            if ($configurableAttributes) {
+                $configurableAttributes = unserialize($configurableAttributes);
+                $configurableAttributes->setProductFilter($product);
+            } else {
+                $configurableAttributes = $this->getConfigurableAttributeCollection($product);
+                $this->extensionAttributesJoinProcessor->process($configurableAttributes);
+                $configurableAttributes->orderByPosition()->load();
+                $this->cache->save(
+                    serialize($configurableAttributes),
+                    $cacheId,
+                    $product->getIdentities()
+                );
+            }
             $product->setData($this->_configurableAttributes, $configurableAttributes);
         }
         \Magento\Framework\Profiler::stop('CONFIGURABLE:' . __METHOD__);
@@ -461,14 +480,6 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
             ['group' => 'CONFIGURABLE', 'method' => __METHOD__]
         );
         if (!$product->hasData($this->_usedProducts)) {
-            if (is_null($requiredAttributeIds) && is_null($product->getData($this->_configurableAttributes))) {
-                // If used products load before attributes, we will load attributes.
-                $this->getConfigurableAttributes($product);
-                // After attributes loading products loaded too.
-                \Magento\Framework\Profiler::stop('CONFIGURABLE:' . __METHOD__);
-                return $product->getData($this->_usedProducts);
-            }
-
             $usedProducts = [];
             $collection = $this->getUsedProductCollection($product)
                 ->addAttributeToSelect('*')
@@ -564,6 +575,8 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
     public function save($product)
     {
         parent::save($product);
+        $cacheId =  __CLASS__ . $product->getId();
+        $this->cache->remove($cacheId);
 
         $extensionAttributes = $product->getExtensionAttributes();
 
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 46579e63d12c104e662cf4da26afb3429906325b..2afb897268a1b4f9fb466df0f7a382050acd2669 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -267,4 +267,25 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
         return $this->setData(self::KEY_PRODUCT_ID, $value);
     }
     //@codeCoverageIgnoreEnd
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            ['metadataPool']
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->metadataPool = $objectManager->get(MetadataPool::class);
+    }
 }
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 4a2233b76a891ab4b9593258c7ef60bce00da763..01a6ad969f320ca8da089139dca0754a74029543 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
@@ -66,19 +66,7 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
         $psExpr = $this->_addAttributeToSelect($select, 'status', 'e.' . $metadata->getLinkField(), 'cs.store_id');
         $psCond = $connection->quoteInto($psExpr . '=?', ProductStatus::STATUS_ENABLED);
 
-        if ($this->_isManageStock()) {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
-                1,
-                'cisi.is_in_stock'
-            );
-        } else {
-            $statusExpr = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
-                'cisi.is_in_stock',
-                1
-            );
-        }
+        $statusExpr = $this->getStatusExpression($connection);
 
         $optExpr = $connection->getCheckSql("{$psCond} AND le.required_options = 0", 'i.stock_status', 0);
         $stockStatusExpr = $connection->getLeastSql(["MAX({$optExpr})", "MIN({$statusExpr})"]);
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 0a11e18b822e0007eefed9205f6f04b27adb70dd..9027608058c368647b5858c5cc3c55e6d9de5e82 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
@@ -7,7 +7,10 @@
  */
 namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute;
 
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute;
 use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Model\Entity\MetadataPool;
 use Magento\Catalog\Api\Data\ProductInterface;
 
@@ -41,7 +44,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
     /**
      * Catalog product type configurable
      *
-     * @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable
+     * @var Configurable
      */
     protected $_productTypeConfigurable;
 
@@ -63,9 +66,9 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
      * @param \Magento\Framework\Event\ManagerInterface $eventManager
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
-     * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable
+     * @param Configurable $catalogProductTypeConfigurable
      * @param \Magento\Catalog\Helper\Data $catalogData
-     * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource
+     * @param Attribute $resource
      * @param MetadataPool $metadataPool
      * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
@@ -76,9 +79,9 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
         \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
         \Magento\Framework\Event\ManagerInterface $eventManager,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
-        \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable,
+        Configurable $catalogProductTypeConfigurable,
         \Magento\Catalog\Helper\Data $catalogData,
-        \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource,
+        Attribute $resource,
         MetadataPool $metadataPool,
         \Magento\Framework\DB\Adapter\AdapterInterface $connection = null
     ) {
@@ -119,7 +122,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
     /**
      * Get product type
      *
-     * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable
+     * @return Configurable
      */
     private function getProductType()
     {
@@ -303,8 +306,38 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
      *
      * @return \Magento\Catalog\Model\Product
      */
-    public function getProduct()
+    private function getProduct()
     {
         return $this->_product;
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            [
+                '_product',
+                '_catalogData',
+                '_productTypeConfigurable',
+                '_storeManager',
+                'metadataPool',
+            ]
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
+        $this->_productTypeConfigurable = $objectManager->get(Configurable::class);
+        $this->_catalogData = $objectManager->get(\Magento\Catalog\Helper\Data::class);
+        $this->metadataPool = $objectManager->get(MetadataPool::class);
+    }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Plugin/Model/Product.php b/app/code/Magento/ConfigurableProduct/Plugin/Model/Product.php
new file mode 100644
index 0000000000000000000000000000000000000000..92870ca91f42c6af17fbb94957f084ba709b8252
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Plugin/Model/Product.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Plugin\Model;
+
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+
+/**
+ * Plugin for Product Identity
+ */
+class Product
+{
+    /**
+     * Add identity of child product to identities
+     *
+     * @param \Magento\Catalog\Model\Product $product
+     * @param string[] $result
+     * @return string[]
+     */
+    public function afterGetIdentities(\Magento\Catalog\Model\Product $product, $result)
+    {
+        /** @var Configurable $productType */
+        $productType = $product->getTypeInstance();
+        if ($productType instanceof Configurable) {
+            foreach ($productType->getUsedProductIds($product) as $productId) {
+                $result[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $productId;
+            }
+        }
+        return $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
index b0f840ac224c24d600e328aa459213e847ee5365..c6081376d59e586a8d737e7f3a61dc7e9e734e53 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php
@@ -11,6 +11,8 @@ 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 Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection;
+use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
 use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
 /**
@@ -52,10 +54,13 @@ class LoaderTest extends \PHPUnit_Framework_TestCase
 
         $this->configurable = $this->getMockBuilder(Configurable::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getConfigurableAttributes'])
+            ->setMethods(['getConfigurableAttributeCollection'])
             ->getMock();
 
-        $this->loader = new Loader($this->optionValueFactory);
+        $extensionAttributesJoinProcessor = $this->getMockBuilder(JoinProcessorInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->loader = new Loader($this->optionValueFactory, $extensionAttributesJoinProcessor);
     }
 
     /**
@@ -75,12 +80,17 @@ class LoaderTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->setMethods(['getOptions', 'setValues'])
             ->getMock();
+
         $attributes = [$attribute];
+        
+        $iterator = $this->getMockBuilder(Collection::class)->disableOriginalConstructor()->getMock();
+        $iterator->expects($this->once())->method('getIterator')
+            ->willReturn(new \ArrayIterator($attributes));
 
         $this->configurable->expects(static::once())
-            ->method('getConfigurableAttributes')
+            ->method('getConfigurableAttributeCollection')
             ->with($this->product)
-            ->willReturn($attributes);
+            ->willReturn($iterator);
 
         $attribute->expects(static::once())
             ->method('getOptions')
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 ef858285e1b8035e30d9867a909ea61b5892fc18..9c2bf6575f683682be8e4d2ee5ac98c57eaf3945 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
@@ -82,6 +82,9 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     protected $entityMetadata;
 
+    /** @var \PHPUnit_Framework_MockObject_MockObject */
+    private $cache;
+
     /**
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
@@ -144,6 +147,8 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             ->method('getMetadata')
             ->with(ProductInterface::class)
             ->willReturn($this->entityMetadata);
+        $this->cache = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class)
+            ->getMockForAbstractClass();
 
         $this->_model = $this->_objectHelper->getObject(
             'Magento\ConfigurableProduct\Model\Product\Type\Configurable',
@@ -159,6 +164,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
                 'logger' => $logger,
                 'productRepository' => $this->productRepository,
                 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock,
+                'cache' => $this->cache,
                 'metadataPool' => $this->metadataPool,
             ]
         );
@@ -440,13 +446,15 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
 
         /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */
         $product = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getData', 'hasData', 'setData'])
+            ->setMethods(['getData', 'hasData', 'setData', 'getIdentities', 'getId'])
             ->disableOriginalConstructor()
             ->getMock();
         $product->expects($this->once())->method('hasData')->with($configurableAttributes)->willReturn(false);
         $product->expects($this->once())->method('setData')->willReturnSelf();
         $product->expects($this->once())->method('getData')->with($configurableAttributes)->willReturn($expectedData);
+        $product->expects($this->once())->method('getIdentities')->willReturn([1,2,3]);
 
+        
         $attributeCollection = $this->getMockBuilder(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection'
         )
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index c0622f73afe82c653383d45e50a5542a6fa9cb76..b3bfa1fb3111afb44d1a332a9323ec563ae8ad82 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -59,6 +59,9 @@
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
         <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/>
     </type>
+    <type name="Magento\Catalog\Model\Product">
+        <plugin name="configurable_identity" type="Magento\ConfigurableProduct\Plugin\Model\Product" />
+    </type>
     <type name="Magento\Catalog\Model\Product\Type">
         <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" />
     </type>
@@ -143,4 +146,9 @@
     <type name="\Magento\ProductVideo\Block\Product\View\Gallery">
         <plugin name="product_video_gallery" type="\Magento\ConfigurableProduct\Block\Plugin\Product\Media\Gallery" />
     </type>
+    <type name="Magento\ConfigurableProduct\Model\Product\Type\Configurable">
+        <arguments>
+            <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Collection</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php b/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
index e582e83f3dcd4b9c2ad19da5307fc9d312ad0cbf..d08150611966309ef10ce500d7adda03489da305 100644
--- a/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
+++ b/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
@@ -642,13 +642,14 @@ class ProcessCronQueueObserverTest extends \PHPUnit_Framework_TestCase
         ];
 
         // This item was scheduled 2 days ago
+        /** @var \Magento\Cron\Model\Schedule|\PHPUnit_Framework_MockObject_MockObject $schedule1 */
         $schedule1 = $this->getMockBuilder(
             'Magento\Cron\Model\Schedule'
         )->disableOriginalConstructor()->setMethods(
             ['getExecutedAt', 'getScheduledAt', 'getStatus', 'delete', '__wakeup']
         )->getMock();
         $schedule1->expects($this->any())->method('getExecutedAt')->will($this->returnValue(null));
-        $schedule1->expects($this->any())->method('getScheduledAt')->will($this->returnValue('-2 day -1 hour'));
+        $schedule1->expects($this->any())->method('getScheduledAt')->will($this->returnValue('-2 day -2 hour'));
         $schedule1->expects($this->any())->method('getStatus')->will($this->returnValue(Schedule::STATUS_MISSED));
         //we expect this job be deleted from the list
         $schedule1->expects($this->once())->method('delete')->will($this->returnValue(true));
diff --git a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php
index ec5419771a6fda20396abf1a5d71a92c4cdbdd81..1b51d24993d9deb5fc1f10d6a9b5eb4c9589d087 100644
--- a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php
+++ b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php
@@ -160,6 +160,7 @@ class XmlCatalogGenerateCommand extends Command
      */
     private function getFormatters($format)
     {
+        $format = strtolower($format);
         if (!isset($this->formats[$format])) {
             return false;
         }
diff --git a/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php b/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php
index 5d2eaa4b8e82ee32e3ae961dbd46cf001e696d7d..32bbcc0f37519620ca52c3061754034596094d44 100644
--- a/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php
+++ b/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php
@@ -36,24 +36,27 @@ class Downloadable extends \Magento\Sales\Block\Order\Email\Items\DefaultItems
     protected $_itemsFactory;
 
     /**
-     * @var \Magento\Framework\UrlInterface
+     * @var \Magento\Framework\Url
      */
-    protected $urlGenerator;
+    protected $frontendUrlBuilder;
 
     /**
      * @param \Magento\Framework\View\Element\Template\Context $context
      * @param \Magento\Downloadable\Model\Link\PurchasedFactory $purchasedFactory
      * @param \Magento\Downloadable\Model\ResourceModel\Link\Purchased\Item\CollectionFactory $itemsFactory
+     * @param \Magento\Framework\Url $frontendUrlBuilder
      * @param array $data
      */
     public function __construct(
         \Magento\Framework\View\Element\Template\Context $context,
         \Magento\Downloadable\Model\Link\PurchasedFactory $purchasedFactory,
         \Magento\Downloadable\Model\ResourceModel\Link\Purchased\Item\CollectionFactory $itemsFactory,
+        \Magento\Framework\Url $frontendUrlBuilder,
         array $data = []
     ) {
         $this->_purchasedFactory = $purchasedFactory;
         $this->_itemsFactory = $itemsFactory;
+        $this->frontendUrlBuilder = $frontendUrlBuilder;
         parent::__construct($context, $data);
     }
 
@@ -94,7 +97,7 @@ class Downloadable extends \Magento\Sales\Block\Order\Email\Items\DefaultItems
      */
     public function getPurchasedLinkUrl($item)
     {
-        return $this->_urlBuilder->getUrl(
+        return $this->frontendUrlBuilder->getUrl(
             'downloadable/download/link',
             [
                 'id' => $item->getLinkHash(),
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php
index 6e056ee9e45a4c76c5701cca37e7d56563dc878c..aef2471617fa4710dd8d00b4bc69a06646d0aade 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php
@@ -90,11 +90,7 @@ class Upload extends \Magento\Downloadable\Controller\Adminhtml\Downloadable\Fil
                 throw new \Exception('File can not be moved from temporary folder to the destination folder.');
             }
 
-            /**
-             * Workaround for prototype 1.7 methods "isJSON", "evalJSON" on Windows OS
-             */
-            $result['tmp_name'] = str_replace('\\', '/', $result['tmp_name']);
-            $result['path'] = str_replace('\\', '/', $result['path']);
+            unset($result['tmp_name'], $result['path']);
 
             if (isset($result['file'])) {
                 $relativePath = rtrim($tmpPath, '/') . '/' . ltrim($result['file'], '/');
diff --git a/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml b/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml
index 9146b0a26a291528840897f43f7969b170dbc577..7d9d0df2b78569ff8af333f237676b074aed10b6 100644
--- a/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml
+++ b/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml
@@ -38,7 +38,9 @@
                     <td data-th="<?php echo $block->escapeHtml(__('Date')) ?>" class="col date"><?php /* @escapeNotVerified */ echo $block->formatDate($_item->getPurchased()->getCreatedAt()) ?></td>
                     <td data-th="<?php echo $block->escapeHtml(__('Title')) ?>" class="col title">
                         <strong class="product-name"><?php echo $block->escapeHtml($_item->getPurchased()->getProductName()) ?></strong>
+                        <?php if ($_item->getStatus() == \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE): ?>
                         <a href="<?php /* @escapeNotVerified */ echo $block->getDownloadUrl($_item) ?>" title="<?php echo $block->escapeHtml(__('Start Download')) ?>" class="action download" <?php echo $block->getIsOpenInNewWindow() ? 'onclick="this.target=\'_blank\'"' : ''; ?>><?php echo $block->escapeHtml($_item->getLinkTitle()) ?></a>
+                        <?php endif; ?>
                     </td>
                     <td data-th="<?php echo $block->escapeHtml(__('Status')) ?>" class="col status"><?php /* @escapeNotVerified */ echo __(ucfirst($_item->getStatus())) ?></td>
                     <td data-th="<?php echo $block->escapeHtml(__('Remaining Downloads')) ?>" class="col remaining"><?php /* @escapeNotVerified */ echo $block->getRemainingDownloads($_item) ?></td>
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index 2c8cc458caa908df9398b9af163efa77e71e36a0..0d1a65d258d6898e59d1092d3ff7d174d36da41f 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -463,4 +463,28 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute\AbstractAttribute im
     {
         return [self::CACHE_TAG . '_' . $this->getId()];
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            ['_localeDate', '_localeResolver', 'reservedAttributeList', 'dateTimeFormatter']
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_localeDate = $objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class);
+        $this->_localeResolver = $objectManager->get(\Magento\Catalog\Model\Product\ReservedAttributeList::class);
+        $this->reservedAttributeList = $objectManager->get(\Magento\Framework\Locale\ResolverInterface::class);
+        $this->dateTimeFormatter = $objectManager->get(DateTimeFormatterInterface::class);
+    }
 }
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index d09156a107ebb934b9165caf6a34ada6b32b39a2..81535a0af1d9f0e31ceb7af5d9862bb3b1f2bd2c 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -1237,4 +1237,45 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens
     {
         return $this->_setExtensionAttributes($extensionAttributes);
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            [
+                '_eavConfig',
+                '_eavTypeFactory',
+                '_storeManager',
+                '_resourceHelper',
+                '_universalFactory',
+                'optionDataFactory',
+                'dataObjectProcessor',
+                'dataObjectHelper',
+                '_entity',
+                '_backend',
+                '_source',
+                '_frontend',
+            ]
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class);
+        $this->_eavTypeFactory = $objectManager->get(\Magento\Eav\Model\Entity\TypeFactory::class);
+        $this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
+        $this->_resourceHelper = $objectManager->get(\Magento\Eav\Model\ResourceModel\Helper::class);
+        $this->_universalFactory = $objectManager->get(\Magento\Framework\Validator\UniversalFactory ::class);
+        $this->optionDataFactory = $objectManager->get(\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory::class);
+        $this->dataObjectProcessor = $objectManager->get(\Magento\Framework\Reflection\DataObjectProcessor::class);
+        $this->dataObjectHelper = $objectManager->get(\Magento\Framework\Api\DataObjectHelper::class);
+    }
 }
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php
index de6a6600fe3af43cf0f5223ba840b36b619eb344..9c98d5299735e1cf33a16190b40de35d33aa4465 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php
@@ -48,20 +48,24 @@ class OptionManagement implements \Magento\Eav\Api\AttributeOptionManagementInte
         if (!$attribute->usesSource()) {
             throw new StateException(__('Attribute %1 doesn\'t work with options', $attributeCode));
         }
-        $key = 'new_option';
+        $optionId = 'new_option';
+        if ($option->getValue()) {
+            $this->validateOption($attribute, $option->getValue());
+            $optionId = $option->getValue();
+        }
 
         $options = [];
-        $options['value'][$key][0] = $option->getLabel();
-        $options['order'][$key] = $option->getSortOrder();
+        $options['value'][$optionId][0] = $option->getLabel();
+        $options['order'][$optionId] = $option->getSortOrder();
 
         if (is_array($option->getStoreLabels())) {
             foreach ($option->getStoreLabels() as $label) {
-                $options['value'][$key][$label->getStoreId()] = $label->getLabel();
+                $options['value'][$optionId][$label->getStoreId()] = $label->getLabel();
             }
         }
 
         if ($option->getIsDefault()) {
-            $attribute->setDefault([$key]);
+            $attribute->setDefault([$optionId]);
         }
 
         $attribute->setOption($options);
@@ -87,12 +91,7 @@ class OptionManagement implements \Magento\Eav\Api\AttributeOptionManagementInte
         if (!$attribute->usesSource()) {
             throw new StateException(__('Attribute %1 doesn\'t have any option', $attributeCode));
         }
-
-        if (!$attribute->getSource()->getOptionText($optionId)) {
-            throw new NoSuchEntityException(
-                __('Attribute %1 does not contain option with Id %2', $attribute->getId(), $optionId)
-            );
-        }
+        $this->validateOption($attribute, $optionId);
 
         $removalMarker = [
             'option' => [
@@ -128,4 +127,19 @@ class OptionManagement implements \Magento\Eav\Api\AttributeOptionManagementInte
 
         return $options;
     }
+
+    /**
+     * @param \Magento\Eav\Api\Data\AttributeInterface $attribute
+     * @param int $optionId
+     * @throws NoSuchEntityException
+     * @return void
+     */
+    protected function validateOption($attribute, $optionId)
+    {
+        if (!$attribute->getSource()->getOptionText($optionId)) {
+            throw new NoSuchEntityException(
+                __('Attribute %1 does not contain option with Id %2', $attribute->getAttributeCode(), $optionId)
+            );
+        }
+    }
 }
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/OptionManagementTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/OptionManagementTest.php
index 622f920afccfa25dbd1871bab1a8b7babf82b5c1..9b9b1ad3b0d4737bebbdb4c0589bd3eaa08a1ef0 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/OptionManagementTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/OptionManagementTest.php
@@ -261,7 +261,7 @@ class OptionManagementTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \Magento\Framework\Exception\NoSuchEntityException
-     * @expectedExceptionMessage Attribute 42 does not contain option with Id option
+     * @expectedExceptionMessage Attribute atrCode does not contain option with Id option
      */
     public function testDeleteWithWrongOption()
     {
@@ -275,14 +275,15 @@ class OptionManagementTest extends \PHPUnit_Framework_TestCase
             false,
             false,
             true,
-            ['usesSource', 'getSource', 'getId', 'getOptionText', 'addData']
+            ['usesSource', 'getSource', 'getAttributeCode']
         );
         $this->attributeRepositoryMock->expects($this->once())->method('get')->with($entityType, $attributeCode)
             ->willReturn($attributeMock);
+        $sourceMock = $this->getMockForAbstractClass('\Magento\Eav\Model\Entity\Attribute\Source\SourceInterface');
+        $sourceMock->expects($this->once())->method('getOptionText')->willReturn(false);
         $attributeMock->expects($this->once())->method('usesSource')->willReturn(true);
-        $attributeMock->expects($this->once())->method('getSource')->willReturnSelf();
-        $attributeMock->expects($this->once())->method('getOptionText')->willReturn(false);
-        $attributeMock->expects($this->once())->method('getId')->willReturn(42);
+        $attributeMock->expects($this->once())->method('getSource')->willReturn($sourceMock);
+        $attributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
         $this->resourceModelMock->expects($this->never())->method('save');
         $this->model->delete($entityType, $attributeCode, $optionId);
     }
diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php
index f8db07e887ca8698b47a8b33dd904cc417d7fa0d..2091c254b98c273b3add428d14514ff03c432c52 100644
--- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php
+++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php
@@ -76,19 +76,7 @@ class Grouped extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stoc
         );
         $productStatusCond = $connection->quoteInto($productStatusExpr . '=?', ProductStatus::STATUS_ENABLED);
 
-        if ($this->_isManageStock()) {
-            $statusExpression = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
-                1,
-                'cisi.is_in_stock'
-            );
-        } else {
-            $statusExpression = $connection->getCheckSql(
-                'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
-                'cisi.is_in_stock',
-                1
-            );
-        }
+        $statusExpression = $this->getStatusExpression($connection);
 
         $optExpr = $connection->getCheckSql("{$productStatusCond} AND le.required_options = 0", 'i.stock_status', 0);
         $stockStatusExpr = $connection->getLeastSql(["MAX({$optExpr})", "MIN({$statusExpression})"]);
diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
index dcd22963ff7ecf411a02cfcc5d83795143dbaead..5a222a8a506c1f8052790fc4e6e3bca9915d1935 100644
--- a/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
+++ b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
@@ -5,14 +5,16 @@
  */
 namespace Magento\OfflineShipping\Model\Carrier;
 
+use Magento\OfflineShipping\Model\Carrier\Flatrate\ItemPriceCalculator;
 use Magento\Quote\Model\Quote\Address\RateRequest;
+use Magento\Shipping\Model\Carrier\AbstractCarrier;
+use Magento\Shipping\Model\Carrier\CarrierInterface;
 use Magento\Shipping\Model\Rate\Result;
 
 /**
  * Flat rate shipping model
  */
-class Flatrate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements
-    \Magento\Shipping\Model\Carrier\CarrierInterface
+class Flatrate extends AbstractCarrier implements CarrierInterface
 {
     /**
      * @var string
@@ -34,12 +36,18 @@ class Flatrate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implement
      */
     protected $_rateMethodFactory;
 
+    /**
+     * @var ItemPriceCalculator
+     */
+    private $itemPriceCalculator;
+
     /**
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
      * @param \Psr\Log\LoggerInterface $logger
      * @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
      * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
+     * @param ItemPriceCalculator $itemPriceCalculator
      * @param array $data
      */
     public function __construct(
@@ -48,10 +56,12 @@ class Flatrate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implement
         \Psr\Log\LoggerInterface $logger,
         \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
         \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
+        \Magento\OfflineShipping\Model\Carrier\Flatrate\ItemPriceCalculator $itemPriceCalculator,
         array $data = []
     ) {
         $this->_rateResultFactory = $rateResultFactory;
         $this->_rateMethodFactory = $rateMethodFactory;
+        $this->itemPriceCalculator = $itemPriceCalculator;
         parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
     }
 
@@ -67,6 +77,28 @@ class Flatrate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implement
             return false;
         }
 
+        $freeBoxes = $this->getFreeBoxesCount($request);
+        $this->setFreeBoxes($freeBoxes);
+
+        /** @var Result $result */
+        $result = $this->_rateResultFactory->create();
+
+        $shippingPrice = $this->getShippingPrice($request, $freeBoxes);
+
+        if ($shippingPrice !== false) {
+            $method = $this->createResultMethod($shippingPrice);
+            $result->append($method);
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param RateRequest $request
+     * @return int
+     */
+    private function getFreeBoxesCount(RateRequest $request)
+    {
         $freeBoxes = 0;
         if ($request->getAllItems()) {
             foreach ($request->getAllItems() as $item) {
@@ -75,64 +107,84 @@ class Flatrate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implement
                 }
 
                 if ($item->getHasChildren() && $item->isShipSeparately()) {
-                    foreach ($item->getChildren() as $child) {
-                        if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
-                            $freeBoxes += $item->getQty() * $child->getQty();
-                        }
-                    }
+                    $freeBoxes += $this->getFreeBoxesCountFromChildren($item);
                 } elseif ($item->getFreeShipping()) {
                     $freeBoxes += $item->getQty();
                 }
             }
         }
-        $this->setFreeBoxes($freeBoxes);
+        return $freeBoxes;
+    }
 
-        /** @var Result $result */
-        $result = $this->_rateResultFactory->create();
-        if ($this->getConfigData('type') == 'O') {
+    /**
+     * @return array
+     */
+    public function getAllowedMethods()
+    {
+        return ['flatrate' => $this->getConfigData('name')];
+    }
+
+    /**
+     * @param RateRequest $request
+     * @param int $freeBoxes
+     * @return bool|float
+     */
+    private function getShippingPrice(RateRequest $request, $freeBoxes)
+    {
+        $shippingPrice = false;
+
+        $configPrice = $this->getConfigData('price');
+        if ($this->getConfigData('type') === 'O') {
             // per order
-            $shippingPrice = $this->getConfigData('price');
-        } elseif ($this->getConfigData('type') == 'I') {
+            $shippingPrice = $this->itemPriceCalculator->getShippingPricePerOrder($request, $configPrice, $freeBoxes);
+        } elseif ($this->getConfigData('type') === 'I') {
             // per item
-            $shippingPrice = $request->getPackageQty() * $this->getConfigData(
-                'price'
-            ) - $this->getFreeBoxes() * $this->getConfigData(
-                'price'
-            );
-        } else {
-            $shippingPrice = false;
+            $shippingPrice = $this->itemPriceCalculator->getShippingPricePerItem($request, $configPrice, $freeBoxes);
         }
 
         $shippingPrice = $this->getFinalPriceWithHandlingFee($shippingPrice);
 
-        if ($shippingPrice !== false) {
-            /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
-            $method = $this->_rateMethodFactory->create();
-
-            $method->setCarrier('flatrate');
-            $method->setCarrierTitle($this->getConfigData('title'));
-
-            $method->setMethod('flatrate');
-            $method->setMethodTitle($this->getConfigData('name'));
+        if ($shippingPrice !== false && (
+                $request->getFreeShipping() === true || $request->getPackageQty() == $freeBoxes
+            )
+        ) {
+            $shippingPrice = '0.00';
+        }
+        return $shippingPrice;
+    }
 
-            if ($request->getFreeShipping() === true || $request->getPackageQty() == $this->getFreeBoxes()) {
-                $shippingPrice = '0.00';
-            }
+    /**
+     * @param int|float $shippingPrice
+     * @return \Magento\Quote\Model\Quote\Address\RateResult\Method
+     */
+    private function createResultMethod($shippingPrice)
+    {
+        /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
+        $method = $this->_rateMethodFactory->create();
 
-            $method->setPrice($shippingPrice);
-            $method->setCost($shippingPrice);
+        $method->setCarrier('flatrate');
+        $method->setCarrierTitle($this->getConfigData('title'));
 
-            $result->append($method);
-        }
+        $method->setMethod('flatrate');
+        $method->setMethodTitle($this->getConfigData('name'));
 
-        return $result;
+        $method->setPrice($shippingPrice);
+        $method->setCost($shippingPrice);
+        return $method;
     }
 
     /**
-     * @return array
+     * @param mixed $item
+     * @return mixed
      */
-    public function getAllowedMethods()
+    private function getFreeBoxesCountFromChildren($item)
     {
-        return ['flatrate' => $this->getConfigData('name')];
+        $freeBoxes = 0;
+        foreach ($item->getChildren() as $child) {
+            if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
+                $freeBoxes += $item->getQty() * $child->getQty();
+            }
+        }
+        return $freeBoxes;
     }
 }
diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate/ItemPriceCalculator.php b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate/ItemPriceCalculator.php
new file mode 100644
index 0000000000000000000000000000000000000000..51b63969817e097ebea8b3f524f8d8e09a168905
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate/ItemPriceCalculator.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\Carrier\Flatrate;
+
+use Magento\Quote\Model\Quote\Address\RateRequest;
+
+class ItemPriceCalculator
+{
+    /**
+     * @param RateRequest $request
+     * @param int $basePrice
+     * @param int $freeBoxes
+     * @return float
+     */
+    public function getShippingPricePerItem(
+        \Magento\Quote\Model\Quote\Address\RateRequest $request,
+        $basePrice,
+        $freeBoxes
+    ) {
+        return $request->getPackageQty() * $basePrice - $freeBoxes * $basePrice;
+    }
+
+    /**
+     * @param RateRequest $request
+     * @param int $basePrice
+     * @param int $freeBoxes
+     * @return float
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function getShippingPricePerOrder(
+        \Magento\Quote\Model\Quote\Address\RateRequest $request,
+        $basePrice,
+        $freeBoxes
+    ) {
+        return $basePrice;
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/Config/Backend/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Config/Backend/Tablerate.php
index 08d23d9245564215089f6a4fb98f0703b901a681..c65993dfe592f842d1b6b82853888516f7792dbd 100644
--- a/app/code/Magento/OfflineShipping/Model/Config/Backend/Tablerate.php
+++ b/app/code/Magento/OfflineShipping/Model/Config/Backend/Tablerate.php
@@ -25,8 +25,8 @@ class Tablerate extends \Magento\Framework\App\Config\Value
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
      * @param \Magento\Framework\App\Cache\TypeListInterface $cacheTypeList
      * @param \Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory $tablerateFactory
-     * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
-     * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
+     * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
+     * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
      * @param array $data
      */
     public function __construct(
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php
index df0228a338358448f9af9d8106af49ed87a5a6ad..82f053fdbe0a77f18ee40330102fe6c44f694426 100644
--- a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php
@@ -13,6 +13,9 @@ namespace Magento\OfflineShipping\Model\ResourceModel\Carrier;
 
 use Magento\Framework\Filesystem;
 use Magento\Framework\Filesystem\DirectoryList;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQuery;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQueryFactory;
 
 /**
  * @SuppressWarnings(PHPMD.TooManyFields)
@@ -87,50 +90,51 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     /**
      * @var \Magento\Framework\App\Config\ScopeConfigInterface
      */
-    protected $_coreConfig;
+    protected $coreConfig;
 
     /**
      * @var \Psr\Log\LoggerInterface
      */
-    protected $_logger;
+    protected $logger;
 
     /**
      * @var \Magento\Store\Model\StoreManagerInterface
      */
-    protected $_storeManager;
+    protected $storeManager;
 
     /**
-     * @var \Magento\OfflineShipping\Model\Carrier\Tablerate
+     * @var \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
      */
-    protected $_carrierTablerate;
+    protected $carrierTablerate;
 
     /**
-     * @var \Magento\Directory\Model\ResourceModel\Country\CollectionFactory
+     * Filesystem instance
+     *
+     * @var \Magento\Framework\Filesystem
      */
-    protected $_countryCollectionFactory;
+    protected $filesystem;
 
     /**
-     * @var \Magento\Directory\Model\ResourceModel\Region\CollectionFactory
+     * @var Import
      */
-    protected $_regionCollectionFactory;
+    private $import;
 
     /**
-     * Filesystem instance
-     *
-     * @var \Magento\Framework\Filesystem
+     * @var RateQueryFactory
      */
-    protected $_filesystem;
+    private $rateQueryFactory;
 
     /**
+     * Tablerate constructor.
      * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
      * @param \Psr\Log\LoggerInterface $logger
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\OfflineShipping\Model\Carrier\Tablerate $carrierTablerate
-     * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory
-     * @param \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory
-     * @param \Magento\Framework\Filesystem $filesystem
-     * @param string $connectionName
+     * @param Filesystem $filesystem
+     * @param RateQueryFactory $rateQueryFactory
+     * @param Import $import
+     * @param null $connectionName
      */
     public function __construct(
         \Magento\Framework\Model\ResourceModel\Db\Context $context,
@@ -138,19 +142,19 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\OfflineShipping\Model\Carrier\Tablerate $carrierTablerate,
-        \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory,
-        \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory,
         \Magento\Framework\Filesystem $filesystem,
+        Import $import,
+        RateQueryFactory $rateQueryFactory,
         $connectionName = null
     ) {
         parent::__construct($context, $connectionName);
-        $this->_coreConfig = $coreConfig;
-        $this->_logger = $logger;
-        $this->_storeManager = $storeManager;
-        $this->_carrierTablerate = $carrierTablerate;
-        $this->_countryCollectionFactory = $countryCollectionFactory;
-        $this->_regionCollectionFactory = $regionCollectionFactory;
-        $this->_filesystem = $filesystem;
+        $this->coreConfig = $coreConfig;
+        $this->logger = $logger;
+        $this->storeManager = $storeManager;
+        $this->carrierTablerate = $carrierTablerate;
+        $this->filesystem = $filesystem;
+        $this->import = $import;
+        $this->rateQueryFactory = $rateQueryFactory;
     }
 
     /**
@@ -172,73 +176,66 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     public function getRate(\Magento\Quote\Model\Quote\Address\RateRequest $request)
     {
         $connection = $this->getConnection();
-        $bind = [
-            ':website_id' => (int)$request->getWebsiteId(),
-            ':country_id' => $request->getDestCountryId(),
-            ':region_id' => (int)$request->getDestRegionId(),
-            ':postcode' => $request->getDestPostcode(),
-        ];
-        $select = $connection->select()->from(
-            $this->getMainTable()
-        )->where(
-            'website_id = :website_id'
-        )->order(
-            ['dest_country_id DESC', 'dest_region_id DESC', 'dest_zip DESC']
-        )->limit(
-            1
-        );
-
-        // Render destination condition
-        $orWhere = '(' . implode(
-            ') OR (',
-            [
-                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = :postcode",
-                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = ''",
-
-                // Handle asterix in dest_zip field
-                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = '*'",
-                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = '*'",
-                "dest_country_id = '0' AND dest_region_id = :region_id AND dest_zip = '*'",
-                "dest_country_id = '0' AND dest_region_id = 0 AND dest_zip = '*'",
-                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = ''",
-                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = :postcode",
-                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = '*'"
-            ]
-        ) . ')';
-        $select->where($orWhere);
-
-        // Render condition by condition name
-        if (is_array($request->getConditionName())) {
-            $orWhere = [];
-            $i = 0;
-            foreach ($request->getConditionName() as $conditionName) {
-                $bindNameKey = sprintf(':condition_name_%d', $i);
-                $bindValueKey = sprintf(':condition_value_%d', $i);
-                $orWhere[] = "(condition_name = {$bindNameKey} AND condition_value <= {$bindValueKey})";
-                $bind[$bindNameKey] = $conditionName;
-                $bind[$bindValueKey] = $request->getData($conditionName);
-                $i++;
-            }
 
-            if ($orWhere) {
-                $select->where(implode(' OR ', $orWhere));
-            }
-        } else {
-            $bind[':condition_name'] = $request->getConditionName();
-            $bind[':condition_value'] = $request->getData($request->getConditionName());
+        $select = $connection->select()->from($this->getMainTable());
+        /** @var RateQuery $rateQuery */
+        $rateQuery = $this->rateQueryFactory->create(['request' => $request]);
 
-            $select->where('condition_name = :condition_name');
-            $select->where('condition_value <= :condition_value');
-        }
+        $rateQuery->prepareSelect($select);
+        $bindings = $rateQuery->getBindings();
 
-        $result = $connection->fetchRow($select, $bind);
+        $result = $connection->fetchRow($select, $bindings);
         // Normalize destination zip code
         if ($result && $result['dest_zip'] == '*') {
             $result['dest_zip'] = '';
         }
+
         return $result;
     }
 
+    /**
+     * @param array $condition
+     * @return $this
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    private function deleteByCondition(array $condition)
+    {
+        $connection = $this->getConnection();
+        $connection->beginTransaction();
+        $connection->delete($this->getMainTable(), $condition);
+        $connection->commit();
+        return $this;
+    }
+
+    /**
+     * @param array $fields
+     * @param array $values
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function importData(array $fields, array $values)
+    {
+        $connection = $this->getConnection();
+        $connection->beginTransaction();
+
+        try {
+            if (count($fields) && count($values)) {
+                $this->getConnection()->insertArray($this->getMainTable(), $fields, $values);
+                $this->_importedRows += count($values);
+            }
+        } catch (\Magento\Framework\Exception\LocalizedException $e) {
+            $connection->rollback();
+            throw new \Magento\Framework\Exception\LocalizedException(__('Unable to import data'), $e);
+        } catch (\Exception $e) {
+            $connection->rollback();
+            $this->logger->critical($e);
+            throw new \Magento\Framework\Exception\LocalizedException(
+                __('Something went wrong while importing table rates.')
+            );
+        }
+        $connection->commit();
+    }
+
     /**
      * Upload table rate file and import data from it
      *
@@ -252,142 +249,72 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      */
     public function uploadAndImport(\Magento\Framework\DataObject $object)
     {
+        /**
+         * @var \Magento\Framework\App\Config\Value $object
+         */
         if (empty($_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'])) {
             return $this;
         }
+        $filePath = $_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'];
 
-        $csvFile = $_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'];
-        $website = $this->_storeManager->getWebsite($object->getScopeId());
-
-        $this->_importWebsiteId = (int)$website->getId();
-        $this->_importUniqueHash = [];
-        $this->_importErrors = [];
-        $this->_importedRows = 0;
-
-        $tmpDirectory = $this->_filesystem->getDirectoryRead(DirectoryList::SYS_TMP);
-        $path = $tmpDirectory->getRelativePath($csvFile);
-        $stream = $tmpDirectory->openFile($path);
-
-        // check and skip headers
-        $headers = $stream->readCsv();
-        if ($headers === false || count($headers) < 5) {
-            $stream->close();
-            throw new \Magento\Framework\Exception\LocalizedException(__('Please correct Table Rates File Format.'));
-        }
-
-        if ($object->getData('groups/tablerate/fields/condition_name/inherit') == '1') {
-            $conditionName = (string)$this->_coreConfig->getValue('carriers/tablerate/condition_name', 'default');
-        } else {
-            $conditionName = $object->getData('groups/tablerate/fields/condition_name/value');
-        }
-        $this->_importConditionName = $conditionName;
-
-        $connection = $this->getConnection();
-        $connection->beginTransaction();
+        $websiteId = $this->storeManager->getWebsite($object->getScopeId())->getId();
+        $conditionName = $this->getConditionName($object);
 
+        $file = $this->getCsvFile($filePath);
         try {
-            $rowNumber = 1;
-            $importData = [];
-
-            $this->_loadDirectoryCountries();
-            $this->_loadDirectoryRegions();
-
             // delete old data by website and condition name
             $condition = [
-                'website_id = ?' => $this->_importWebsiteId,
-                'condition_name = ?' => $this->_importConditionName,
+                'website_id = ?' => $websiteId,
+                'condition_name = ?' => $conditionName,
             ];
-            $connection->delete($this->getMainTable(), $condition);
-
-            while (false !== ($csvLine = $stream->readCsv())) {
-                $rowNumber++;
-
-                if (empty($csvLine)) {
-                    continue;
-                }
-
-                $row = $this->_getImportRow($csvLine, $rowNumber);
-                if ($row !== false) {
-                    $importData[] = $row;
-                }
+            $this->deleteByCondition($condition);
 
-                if (count($importData) == 5000) {
-                    $this->_saveImportData($importData);
-                    $importData = [];
-                }
+            $columns = $this->import->getColumns();
+            $conditionFullName = $this->_getConditionFullName($conditionName);
+            foreach ($this->import->getData($file, $websiteId, $conditionName, $conditionFullName) as $bunch) {
+                $this->importData($columns, $bunch);
             }
-            $this->_saveImportData($importData);
-            $stream->close();
-        } catch (\Magento\Framework\Exception\LocalizedException $e) {
-            $connection->rollback();
-            $stream->close();
-            throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()));
         } catch (\Exception $e) {
-            $connection->rollback();
-            $stream->close();
-            $this->_logger->critical($e);
+            $this->logger->critical($e);
             throw new \Magento\Framework\Exception\LocalizedException(
                 __('Something went wrong while importing table rates.')
             );
+        } finally {
+            $file->close();
         }
 
-        $connection->commit();
-
-        if ($this->_importErrors) {
+        if ($this->import->hasErrors()) {
             $error = __(
                 'We couldn\'t import this file because of these errors: %1',
-                implode(" \n", $this->_importErrors)
+                implode(" \n", $this->import->getErrors())
             );
             throw new \Magento\Framework\Exception\LocalizedException($error);
         }
-
-        return $this;
     }
 
     /**
-     * Load directory countries
-     *
-     * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
+     * @param \Magento\Framework\DataObject $object
+     * @return mixed|string
      */
-    protected function _loadDirectoryCountries()
+    public function getConditionName(\Magento\Framework\DataObject $object)
     {
-        if ($this->_importIso2Countries !== null && $this->_importIso3Countries !== null) {
-            return $this;
-        }
-
-        $this->_importIso2Countries = [];
-        $this->_importIso3Countries = [];
-
-        /** @var $collection \Magento\Directory\Model\ResourceModel\Country\Collection */
-        $collection = $this->_countryCollectionFactory->create();
-        foreach ($collection->getData() as $row) {
-            $this->_importIso2Countries[$row['iso2_code']] = $row['country_id'];
-            $this->_importIso3Countries[$row['iso3_code']] = $row['country_id'];
+        if ($object->getData('groups/tablerate/fields/condition_name/inherit') == '1') {
+            $conditionName = (string)$this->coreConfig->getValue('carriers/tablerate/condition_name', 'default');
+        } else {
+            $conditionName = $object->getData('groups/tablerate/fields/condition_name/value');
         }
-
-        return $this;
+        return $conditionName;
     }
 
     /**
-     * Load directory regions
-     *
-     * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
+     * @param string $filePath
+     * @return \Magento\Framework\Filesystem\File\ReadInterface
      */
-    protected function _loadDirectoryRegions()
+    private function getCsvFile($filePath)
     {
-        if ($this->_importRegions !== null) {
-            return $this;
-        }
-
-        $this->_importRegions = [];
-
-        /** @var $collection \Magento\Directory\Model\ResourceModel\Region\Collection */
-        $collection = $this->_regionCollectionFactory->create();
-        foreach ($collection->getData() as $row) {
-            $this->_importRegions[$row['country_id']][$row['code']] = (int)$row['region_id'];
-        }
-
-        return $this;
+        $tmpDirectory = $this->filesystem->getDirectoryRead(DirectoryList::SYS_TMP);
+        $path = $tmpDirectory->getRelativePath($filePath);
+        return $tmpDirectory->openFile($path);
     }
 
     /**
@@ -399,110 +326,13 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     protected function _getConditionFullName($conditionName)
     {
         if (!isset($this->_conditionFullNames[$conditionName])) {
-            $name = $this->_carrierTablerate->getCode('condition_name_short', $conditionName);
+            $name = $this->carrierTablerate->getCode('condition_name_short', $conditionName);
             $this->_conditionFullNames[$conditionName] = $name;
         }
 
         return $this->_conditionFullNames[$conditionName];
     }
 
-    /**
-     * Validate row for import and return table rate array or false
-     * Error will be add to _importErrors array
-     *
-     * @param array $row
-     * @param int $rowNumber
-     * @return array|false
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-     * @SuppressWarnings(PHPMD.NPathComplexity)
-     */
-    protected function _getImportRow($row, $rowNumber = 0)
-    {
-        // validate row
-        if (count($row) < 5) {
-            $this->_importErrors[] = __('Please correct Table Rates format in the Row #%1.', $rowNumber);
-            return false;
-        }
-
-        // strip whitespace from the beginning and end of each row
-        foreach ($row as $k => $v) {
-            $row[$k] = trim($v);
-        }
-
-        // validate country
-        if (isset($this->_importIso2Countries[$row[0]])) {
-            $countryId = $this->_importIso2Countries[$row[0]];
-        } elseif (isset($this->_importIso3Countries[$row[0]])) {
-            $countryId = $this->_importIso3Countries[$row[0]];
-        } elseif ($row[0] == '*' || $row[0] == '') {
-            $countryId = '0';
-        } else {
-            $this->_importErrors[] = __('Please correct Country "%1" in the Row #%2.', $row[0], $rowNumber);
-            return false;
-        }
-
-        // validate region
-        if ($countryId != '0' && isset($this->_importRegions[$countryId][$row[1]])) {
-            $regionId = $this->_importRegions[$countryId][$row[1]];
-        } elseif ($row[1] == '*' || $row[1] == '') {
-            $regionId = 0;
-        } else {
-            $this->_importErrors[] = __('Please correct Region/State "%1" in the Row #%2.', $row[1], $rowNumber);
-            return false;
-        }
-
-        // detect zip code
-        if ($row[2] == '*' || $row[2] == '') {
-            $zipCode = '*';
-        } else {
-            $zipCode = $row[2];
-        }
-
-        // validate condition value
-        $value = $this->_parseDecimalValue($row[3]);
-        if ($value === false) {
-            $this->_importErrors[] = __(
-                'Please correct %1 "%2" in the Row #%3.',
-                $this->_getConditionFullName($this->_importConditionName),
-                $row[3],
-                $rowNumber
-            );
-            return false;
-        }
-
-        // validate price
-        $price = $this->_parseDecimalValue($row[4]);
-        if ($price === false) {
-            $this->_importErrors[] = __('Please correct Shipping Price "%1" in the Row #%2.', $row[4], $rowNumber);
-            return false;
-        }
-
-        // protect from duplicate
-        $hash = sprintf("%s-%d-%s-%F", $countryId, $regionId, $zipCode, $value);
-        if (isset($this->_importUniqueHash[$hash])) {
-            $this->_importErrors[] = __(
-                'Duplicate Row #%1 (Country "%2", Region/State "%3", Zip "%4" and Value "%5")',
-                $rowNumber,
-                $row[0],
-                $row[1],
-                $zipCode,
-                $value
-            );
-            return false;
-        }
-        $this->_importUniqueHash[$hash] = true;
-
-        return [
-            $this->_importWebsiteId,    // website_id
-            $countryId,                 // dest_country_id
-            $regionId,                  // dest_region_id,
-            $zipCode,                   // dest_zip
-            $this->_importConditionName,// condition_name,
-            $value,                     // condition_value
-            $price                      // price
-        ];
-    }
-
     /**
      * Save import data batch
      *
@@ -527,23 +357,4 @@ class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
 
         return $this;
     }
-
-    /**
-     * Parse and validate positive decimal value
-     * Return false if value is not decimal or is not positive
-     *
-     * @param string $value
-     * @return bool|float
-     */
-    protected function _parseDecimalValue($value)
-    {
-        if (!is_numeric($value)) {
-            return false;
-        }
-        $value = (double)sprintf('%.4F', $value);
-        if ($value < 0.0000) {
-            return false;
-        }
-        return $value;
-    }
 }
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnNotFoundException.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..8cb6cda142c0f78c8a1b975d10923b6c03f399a0
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnNotFoundException.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+use Magento\Framework\Exception\LocalizedException;
+
+class ColumnNotFoundException extends LocalizedException
+{
+
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolver.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolver.php
new file mode 100644
index 0000000000000000000000000000000000000000..c1782b6b9a9e6b153c311341f4c123c571688f5c
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolver.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+class ColumnResolver
+{
+    const COLUMN_COUNTRY = 'Country';
+    const COLUMN_REGION = 'Region/State';
+    const COLUMN_ZIP = 'Zip/Postal Code';
+    const COLUMN_WEIGHT = 'Weight (and above)';
+    const COLUMN_WEIGHT_DESTINATION = 'Weight (and above)';
+    const COLUMN_PRICE = 'Shipping Price';
+
+    /**
+     * @var array
+     */
+    private $nameToPositionIdMap = [
+        self::COLUMN_COUNTRY => 0,
+        self::COLUMN_REGION => 1,
+        self::COLUMN_ZIP => 2,
+        self::COLUMN_WEIGHT => 3,
+        self::COLUMN_WEIGHT_DESTINATION => 3,
+        self::COLUMN_PRICE => 4,
+    ];
+
+    /**
+     * @var array
+     */
+    private $headers;
+
+    /**
+     * ColumnResolver constructor.
+     * @param array $headers
+     * @param array $columns
+     */
+    public function __construct(array $headers, array $columns = [])
+    {
+        $this->nameToPositionIdMap = array_merge($this->nameToPositionIdMap, $columns);
+        $this->headers = array_map('trim', $headers);
+    }
+
+    /**
+     * @param string $column
+     * @param array $values
+     * @return string|int|float|null
+     * @throws ColumnNotFoundException
+     */
+    public function getColumnValue($column, array $values)
+    {
+        $column = (string) $column;
+        $columnIndex = array_search($column, $this->headers, true);
+        if (false === $columnIndex) {
+            if (array_key_exists($column, $this->nameToPositionIdMap)) {
+                $columnIndex = $this->nameToPositionIdMap[$column];
+            } else {
+                throw new ColumnNotFoundException(__('Requested column "%1" cannot be resolved', $column));
+            }
+        }
+
+        if (!array_key_exists($columnIndex, $values)) {
+            throw new ColumnNotFoundException(__('Column "%1" not found', $column));
+        }
+
+        return  trim($values[$columnIndex]);
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowException.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowException.php
new file mode 100644
index 0000000000000000000000000000000000000000..a97bda4ec7d206e85003c9dc641a38d7bf1cb612
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowException.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+use Magento\Framework\Exception\LocalizedException;
+
+class RowException extends LocalizedException
+{
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowParser.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowParser.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c1624f06005c85ad735ab533bfee20cec894c8a
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/CSV/RowParser.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+use Magento\Framework\Phrase;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\LocationDirectory;
+
+class RowParser
+{
+    /**
+     * @var LocationDirectory
+     */
+    private $locationDirectory;
+
+    /**
+     * RowParser constructor.
+     * @param LocationDirectory $locationDirectory
+     */
+    public function __construct(LocationDirectory $locationDirectory)
+    {
+        $this->locationDirectory = $locationDirectory;
+    }
+
+    /**
+     * @return array
+     */
+    public function getColumns()
+    {
+        return [
+            'website_id',
+            'dest_country_id',
+            'dest_region_id',
+            'dest_zip',
+            'condition_name',
+            'condition_value',
+            'price',
+        ];
+    }
+
+    /**
+     * @param array $rowData
+     * @param int $rowNumber
+     * @param int $websiteId
+     * @param string $conditionShortName
+     * @param string $conditionFullName
+     * @param ColumnResolver $columnResolver
+     * @return array
+     * @throws ColumnNotFoundException
+     * @throws RowException
+     */
+    public function parse(
+        array $rowData,
+        $rowNumber,
+        $websiteId,
+        $conditionShortName,
+        $conditionFullName,
+        ColumnResolver $columnResolver
+    ) {
+        // validate row
+        if (count($rowData) < 5) {
+            throw new RowException(__('Please correct Table Rates format in the Row #%1.', $rowNumber));
+        }
+
+        $countryId = $this->getCountryId($rowData, $rowNumber, $columnResolver);
+        $regionId = $this->getRegionId($rowData, $rowNumber, $columnResolver, $countryId);
+        $zipCode = $this->getZipCode($rowData, $columnResolver);
+        $conditionValue = $this->getConditionValue($rowData, $rowNumber, $conditionFullName, $columnResolver);
+        $price = $this->getPrice($rowData, $rowNumber, $columnResolver);
+
+        return [
+            'website_id' => $websiteId,
+            'dest_country_id' => $countryId,
+            'dest_region_id' => $regionId,
+            'dest_zip' => $zipCode,
+            'condition_name' => $conditionShortName,
+            'condition_value' => $conditionValue,
+            'price' => $price,
+        ];
+    }
+
+    /**
+     * @param array $rowData
+     * @param int $rowNumber
+     * @param ColumnResolver $columnResolver
+     * @return null|string
+     * @throws ColumnNotFoundException
+     * @throws RowException
+     */
+    private function getCountryId(array $rowData, $rowNumber, ColumnResolver $columnResolver)
+    {
+        $countryCode = $columnResolver->getColumnValue(ColumnResolver::COLUMN_COUNTRY, $rowData);
+        // validate country
+        if ($this->locationDirectory->hasCountryId($countryCode)) {
+            $countryId = $this->locationDirectory->getCountryId($countryCode);
+        } elseif ($countryCode === '*' || $countryCode === '') {
+            $countryId = '0';
+        } else {
+            throw new RowException(__('Please correct Country "%1" in the Row #%2.', $countryCode, $rowNumber));
+        }
+        return $countryId;
+    }
+
+    /**
+     * @param array $rowData
+     * @param int $rowNumber
+     * @param ColumnResolver $columnResolver
+     * @param int $countryId
+     * @return int|string
+     * @throws ColumnNotFoundException
+     * @throws RowException
+     */
+    private function getRegionId(array $rowData, $rowNumber, ColumnResolver $columnResolver, $countryId)
+    {
+        $regionCode = $columnResolver->getColumnValue(ColumnResolver::COLUMN_REGION, $rowData);
+        if ($countryId !== '0' && $this->locationDirectory->hasRegionId($countryId, $regionCode)) {
+            $regionId = $this->locationDirectory->getRegionId($countryId, $regionCode);
+        } elseif ($regionCode === '*' || $regionCode === '') {
+            $regionId = 0;
+        } else {
+            throw new RowException(__('Please correct Region/State "%1" in the Row #%2.', $regionCode, $rowNumber));
+        }
+        return $regionId;
+    }
+
+    /**
+     * @param array $rowData
+     * @param ColumnResolver $columnResolver
+     * @return float|int|null|string
+     * @throws ColumnNotFoundException
+     */
+    private function getZipCode(array $rowData, ColumnResolver $columnResolver)
+    {
+        $zipCode = $columnResolver->getColumnValue(ColumnResolver::COLUMN_ZIP, $rowData);
+        if ($zipCode === '') {
+            $zipCode = '*';
+        }
+        return $zipCode;
+    }
+
+    /**
+     * @param array $rowData
+     * @param int $rowNumber
+     * @param string $conditionFullName
+     * @param ColumnResolver $columnResolver
+     * @return bool|float
+     * @throws ColumnNotFoundException
+     * @throws RowException
+     */
+    private function getConditionValue(array $rowData, $rowNumber, $conditionFullName, ColumnResolver $columnResolver)
+    {
+        // validate condition value
+        $conditionValue = $columnResolver->getColumnValue($conditionFullName, $rowData);
+        $value = $this->_parseDecimalValue($conditionValue);
+        if ($value === false) {
+            throw new RowException(
+                __(
+                    'Please correct %1 "%2" in the Row #%3.',
+                    $conditionFullName,
+                    $conditionValue,
+                    $rowNumber
+                )
+            );
+        }
+        return $value;
+    }
+
+    /**
+     * @param array $rowData
+     * @param int $rowNumber
+     * @param ColumnResolver $columnResolver
+     * @return bool|float
+     * @throws ColumnNotFoundException
+     * @throws RowException
+     */
+    private function getPrice(array $rowData, $rowNumber, ColumnResolver $columnResolver)
+    {
+        $priceValue = $columnResolver->getColumnValue(ColumnResolver::COLUMN_PRICE, $rowData);
+        $price = $this->_parseDecimalValue($priceValue);
+        if ($price === false) {
+            throw new RowException(__('Please correct Shipping Price "%1" in the Row #%2.', $priceValue, $rowNumber));
+        }
+        return $price;
+    }
+
+    /**
+     * Parse and validate positive decimal value
+     * Return false if value is not decimal or is not positive
+     *
+     * @param string $value
+     * @return bool|float
+     */
+    private function _parseDecimalValue($value)
+    {
+        $result = false;
+        if (is_numeric($value)) {
+            $value = (double)sprintf('%.4F', $value);
+            if ($value >= 0.0000) {
+                $result = $value;
+            }
+        }
+        return $result;
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/DataHashGenerator.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/DataHashGenerator.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ef8d09eab7371d55e956d913f7d47b68630c666
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/DataHashGenerator.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
+
+class DataHashGenerator
+{
+    /**
+     * @param array $data
+     * @return string
+     */
+    public function getHash(array $data)
+    {
+        $countryId = $data['dest_country_id'];
+        $regionId = $data['dest_region_id'];
+        $zipCode = $data['dest_zip'];
+        $conditionValue = $data['condition_value'];
+
+        return sprintf("%s-%d-%s-%F", $countryId, $regionId, $zipCode, $conditionValue);
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/Import.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/Import.php
new file mode 100644
index 0000000000000000000000000000000000000000..00d13fdf7737f6b361614568867643abd0c6fefc
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/Import.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
+
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Filesystem\File\ReadInterface;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolver;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolverFactory;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowException;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowParser;
+use Magento\Store\Model\StoreManagerInterface;
+
+class Import
+{
+    /**
+     * @var StoreManagerInterface
+     */
+    private $storeManager;
+
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    /**
+     * @var ScopeConfigInterface
+     */
+    private $coreConfig;
+
+    /**
+     * @var array
+     */
+    private $errors = [];
+
+    /**
+     * @var CSV\RowParser
+     */
+    private $rowParser;
+
+    /**
+     * @var CSV\ColumnResolverFactory
+     */
+    private $columnResolverFactory;
+
+    /**
+     * @var DataHashGenerator
+     */
+    private $dataHashGenerator;
+
+    /**
+     * @var array
+     */
+    private $uniqueHash = [];
+
+    /**
+     * Import constructor.
+     * @param StoreManagerInterface $storeManager
+     * @param Filesystem $filesystem
+     * @param ScopeConfigInterface $coreConfig
+     * @param CSV\RowParser $rowParser
+     * @param CSV\ColumnResolverFactory $columnResolverFactory
+     * @param DataHashGenerator $dataHashGenerator
+     */
+    public function __construct(
+        StoreManagerInterface $storeManager,
+        Filesystem $filesystem,
+        ScopeConfigInterface $coreConfig,
+        RowParser $rowParser,
+        ColumnResolverFactory $columnResolverFactory,
+        DataHashGenerator $dataHashGenerator
+    ) {
+        $this->storeManager = $storeManager;
+        $this->filesystem = $filesystem;
+        $this->coreConfig = $coreConfig;
+        $this->rowParser = $rowParser;
+        $this->columnResolverFactory = $columnResolverFactory;
+        $this->dataHashGenerator = $dataHashGenerator;
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasErrors()
+    {
+        return (bool)count($this->getErrors());
+    }
+
+    /**
+     * @return array
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+
+    /**
+     * @return array
+     */
+    public function getColumns()
+    {
+        return $this->rowParser->getColumns();
+    }
+
+    /**
+     * @param ReadInterface $file
+     * @param int $websiteId
+     * @param string $conditionShortName
+     * @param string $conditionFullName
+     * @param int $bunchSize
+     * @return \Generator
+     * @throws LocalizedException
+     */
+    public function getData(ReadInterface $file, $websiteId, $conditionShortName, $conditionFullName, $bunchSize = 5000)
+    {
+        $this->errors = [];
+
+        $headers = $this->getHeaders($file);
+        /** @var ColumnResolver $columnResolver */
+        $columnResolver = $this->columnResolverFactory->create(['headers' => $headers]);
+
+        $rowNumber = 1;
+        $items = [];
+        while (false !== ($csvLine = $file->readCsv())) {
+            try {
+                if (empty($csvLine)) {
+                    continue;
+                }
+                $rowData = $this->rowParser->parse(
+                    $csvLine,
+                    ++$rowNumber,
+                    $websiteId,
+                    $conditionShortName,
+                    $conditionFullName,
+                    $columnResolver
+                );
+
+                // protect from duplicate
+                $hash = $this->dataHashGenerator->getHash($rowData);
+                if (array_key_exists($hash, $this->uniqueHash)) {
+                    throw new RowException(
+                        __(
+                            'Duplicate Row #%1 (duplicates row #%2)',
+                            $rowNumber,
+                            $this->uniqueHash[$hash]
+                        )
+                    );
+                }
+                $this->uniqueHash[$hash] = $csvLine;
+
+                $items[] = $rowData;
+                if (count($items) === $bunchSize) {
+                    yield $items;
+                    $items = [];
+                }
+            } catch (RowException $e) {
+                $this->errors[] = $e->getMessage();
+            }
+        }
+        if (count($items)) {
+            yield $items;
+        }
+    }
+
+    /**
+     * @param ReadInterface $file
+     * @return array|bool
+     * @throws LocalizedException
+     */
+    private function getHeaders(ReadInterface $file)
+    {
+        // check and skip headers
+        $headers = $file->readCsv();
+        if ($headers === false || count($headers) < 5) {
+            throw new LocalizedException(__('Please correct Table Rates File Format.'));
+        }
+        return $headers;
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/LocationDirectory.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/LocationDirectory.php
new file mode 100644
index 0000000000000000000000000000000000000000..a3a7d9e8c78942c6f0eac52228131e083e4b09c1
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/LocationDirectory.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
+
+class LocationDirectory
+{
+    /**
+     * @var array
+     */
+    protected $regions;
+
+    /**
+     * @var array
+     */
+    protected $iso2Countries;
+
+    /**
+     * @var array
+     */
+    protected $iso3Countries;
+
+    /**
+     * @var \Magento\Directory\Model\ResourceModel\Country\CollectionFactory
+     */
+    protected $_countryCollectionFactory;
+
+    /**
+     * @var \Magento\Directory\Model\ResourceModel\Region\CollectionFactory
+     */
+    protected $_regionCollectionFactory;
+
+    /**
+     * LocationDirectory constructor.
+     * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory
+     * @param \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory
+     */
+    public function __construct(
+        \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory,
+        \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory
+    ) {
+        $this->_countryCollectionFactory = $countryCollectionFactory;
+        $this->_regionCollectionFactory = $regionCollectionFactory;
+    }
+
+    /**
+     * @param string $countryCode
+     * @return null|string
+     */
+    public function getCountryId($countryCode)
+    {
+        $this->loadCountries();
+        $countryId = null;
+        if (isset($this->iso2Countries[$countryCode])) {
+            $countryId = $this->iso2Countries[$countryCode];
+        } elseif (isset($this->iso3Countries[$countryCode])) {
+            $countryId = $this->iso3Countries[$countryCode];
+        }
+
+        return $countryId;
+    }
+
+    /**
+     * Load directory countries
+     *
+     * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
+     */
+    protected function loadCountries()
+    {
+        if ($this->iso2Countries !== null && $this->iso3Countries !== null) {
+            return $this;
+        }
+
+        $this->iso2Countries = [];
+        $this->iso3Countries = [];
+
+        /** @var $collection \Magento\Directory\Model\ResourceModel\Country\Collection */
+        $collection = $this->_countryCollectionFactory->create();
+        foreach ($collection->getData() as $row) {
+            $this->iso2Countries[$row['iso2_code']] = $row['country_id'];
+            $this->iso3Countries[$row['iso3_code']] = $row['country_id'];
+        }
+
+        return $this;
+    }
+
+    /**
+     * @param string $countryCode
+     * @return bool
+     */
+    public function hasCountryId($countryCode)
+    {
+        $this->loadCountries();
+        return isset($this->iso2Countries[$countryCode]) || isset($this->iso3Countries[$countryCode]);
+    }
+
+    /**
+     * @param string $countryId
+     * @param string $regionCode
+     * @return bool
+     */
+    public function hasRegionId($countryId, $regionCode)
+    {
+        $this->loadRegions();
+        return isset($this->regions[$countryId][$regionCode]);
+    }
+
+    /**
+     * Load directory regions
+     *
+     * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
+     */
+    protected function loadRegions()
+    {
+        if ($this->regions !== null) {
+            return $this;
+        }
+
+        $this->regions = [];
+
+        /** @var $collection \Magento\Directory\Model\ResourceModel\Region\Collection */
+        $collection = $this->_regionCollectionFactory->create();
+        foreach ($collection->getData() as $row) {
+            $this->regions[$row['country_id']][$row['code']] = (int)$row['region_id'];
+        }
+
+        return $this;
+    }
+
+    /**
+     * @param int $countryId
+     * @param string $regionCode
+     * @return string
+     */
+    public function getRegionId($countryId, $regionCode)
+    {
+        $this->loadRegions();
+        return $this->regions[$countryId][$regionCode];
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/RateQuery.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/RateQuery.php
new file mode 100644
index 0000000000000000000000000000000000000000..1c4d87dd11c9f9ad6051d958f62b8422df255efb
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate/RateQuery.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
+
+class RateQuery
+{
+    /**
+     * @var \Magento\Quote\Model\Quote\Address\RateRequest
+     */
+    private $request;
+
+    /**
+     * RateQuery constructor.
+     * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
+     */
+    public function __construct(
+        \Magento\Quote\Model\Quote\Address\RateRequest $request
+    ) {
+        $this->request = $request;
+    }
+
+    /**
+     * @param \Magento\Framework\DB\Select $select
+     * @return \Magento\Framework\DB\Select
+     */
+    public function prepareSelect(\Magento\Framework\DB\Select $select)
+    {
+        $select->where(
+            'website_id = :website_id'
+        )->order(
+            ['dest_country_id DESC', 'dest_region_id DESC', 'dest_zip DESC']
+        )->limit(
+            1
+        );
+
+        // Render destination condition
+        $orWhere = '(' . implode(
+            ') OR (',
+            [
+                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = :postcode",
+                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = ''",
+
+                // Handle asterisk in dest_zip field
+                "dest_country_id = :country_id AND dest_region_id = :region_id AND dest_zip = '*'",
+                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = '*'",
+                "dest_country_id = '0' AND dest_region_id = :region_id AND dest_zip = '*'",
+                "dest_country_id = '0' AND dest_region_id = 0 AND dest_zip = '*'",
+                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = ''",
+                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = :postcode",
+                "dest_country_id = :country_id AND dest_region_id = 0 AND dest_zip = '*'"
+            ]
+        ) . ')';
+        $select->where($orWhere);
+
+        // Render condition by condition name
+        if (is_array($this->request->getConditionName())) {
+            $orWhere = [];
+            foreach (range(0, count($this->request->getConditionName())) as $conditionNumber) {
+                $bindNameKey = sprintf(':condition_name_%d', $conditionNumber);
+                $bindValueKey = sprintf(':condition_value_%d', $conditionNumber);
+                $orWhere[] = "(condition_name = {$bindNameKey} AND condition_value <= {$bindValueKey})";
+            }
+
+            if ($orWhere) {
+                $select->where(implode(' OR ', $orWhere));
+            }
+        } else {
+            $select->where('condition_name = :condition_name');
+            $select->where('condition_value <= :condition_value');
+        }
+        return $select;
+    }
+
+    /**
+     * @return array
+     */
+    public function getBindings()
+    {
+        $bind = [
+            ':website_id' => (int)$this->request->getWebsiteId(),
+            ':country_id' => $this->request->getDestCountryId(),
+            ':region_id' => (int)$this->request->getDestRegionId(),
+            ':postcode' => $this->request->getDestPostcode(),
+        ];
+
+        // Render condition by condition name
+        if (is_array($this->request->getConditionName())) {
+            $i = 0;
+            foreach ($this->request->getConditionName() as $conditionName) {
+                $bindNameKey = sprintf(':condition_name_%d', $i);
+                $bindValueKey = sprintf(':condition_value_%d', $i);
+                $bind[$bindNameKey] = $conditionName;
+                $bind[$bindValueKey] = $this->request->getData($conditionName);
+                $i++;
+            }
+        } else {
+            $bind[':condition_name'] = $this->request->getConditionName();
+            $bind[':condition_value'] = $this->request->getData($this->request->getConditionName());
+        }
+
+        return $bind;
+    }
+
+    /**
+     * @return \Magento\Quote\Model\Quote\Address\RateRequest
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolverTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolverTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..980204d7dab560c6414f5fb758242c221e36f973
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/ColumnResolverTest.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Test\Unit\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolver;
+
+/**
+ * Unit test for Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolver
+ */
+class ColumnResolverTest extends \PHPUnit_Framework_TestCase
+{
+    const CUSTOM_FIELD = 'custom_field';
+
+    private $values = [
+        ColumnResolver::COLUMN_COUNTRY => 'country value',
+        ColumnResolver::COLUMN_REGION => 'region value',
+        ColumnResolver::COLUMN_ZIP => 'zip_value',
+        ColumnResolver::COLUMN_WEIGHT => 'weight_value',
+        ColumnResolver::COLUMN_WEIGHT_DESTINATION => 'weight_destination_value',
+        ColumnResolver::COLUMN_PRICE => 'price_value',
+        self::CUSTOM_FIELD => 'custom_value',
+    ];
+
+    /**
+     * @param $column
+     * @param $expectedValue
+     * @throws \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnNotFoundException
+     * @dataProvider getColumnValueDataProvider
+     */
+    public function testGetColumnValueByPosition($column, $expectedValue)
+    {
+        $headers = array_keys($this->values);
+        $headers = [];
+        $columnResolver = $this->createColumnResolver($headers);
+        $values = array_values($this->values);
+        $result = $columnResolver->getColumnValue($column, $values);
+        $this->assertEquals($expectedValue, $result);
+    }
+
+    /**
+     * @param array $headers
+     * @param array $columns
+     * @return ColumnResolver
+     */
+    private function createColumnResolver(array $headers = [], array $columns = [])
+    {
+        return new ColumnResolver($headers, $columns);
+    }
+
+    /**
+     * @return void
+     * @dataProvider getColumnValueWithCustomHeaderDataProvider
+     */
+    public function testGetColumnValueByHeader($column, $expectedValue)
+    {
+        $reversedValues = array_reverse($this->values);
+        $headers = array_keys($reversedValues);
+        $values = array_values($reversedValues);
+        $columnResolver = $this->createColumnResolver($headers);
+        $result = $columnResolver->getColumnValue($column, $values);
+        $this->assertEquals($expectedValue, $result);
+    }
+
+    /**
+     * @return array
+     */
+    public function getColumnValueDataProvider()
+    {
+        return [
+            ColumnResolver::COLUMN_COUNTRY => [
+                ColumnResolver::COLUMN_COUNTRY,
+                $this->values[ColumnResolver::COLUMN_COUNTRY],
+            ],
+            ColumnResolver::COLUMN_REGION => [
+                ColumnResolver::COLUMN_REGION,
+                $this->values[ColumnResolver::COLUMN_REGION],
+            ],
+            ColumnResolver::COLUMN_ZIP => [
+                ColumnResolver::COLUMN_ZIP,
+                $this->values[ColumnResolver::COLUMN_ZIP],
+            ],
+            ColumnResolver::COLUMN_WEIGHT => [
+                ColumnResolver::COLUMN_WEIGHT,
+                $this->values[ColumnResolver::COLUMN_WEIGHT],
+            ],
+            ColumnResolver::COLUMN_WEIGHT_DESTINATION => [
+                ColumnResolver::COLUMN_WEIGHT_DESTINATION,
+                $this->values[ColumnResolver::COLUMN_WEIGHT_DESTINATION],
+            ],
+            ColumnResolver::COLUMN_PRICE => [
+                ColumnResolver::COLUMN_PRICE,
+                $this->values[ColumnResolver::COLUMN_PRICE],
+            ]
+        ];
+    }
+
+    /**
+     * @return array
+     */
+    public function getColumnValueWithCustomHeaderDataProvider()
+    {
+        $customField = [
+            self::CUSTOM_FIELD => [
+                self::CUSTOM_FIELD,
+                $this->values[self::CUSTOM_FIELD],
+            ],
+        ];
+        return array_merge($this->getColumnValueDataProvider(), $customField);
+    }
+
+    /**
+     * @throws \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnNotFoundException
+     * @expectedException \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnNotFoundException
+     * @expectedExceptionMessage Requested column "custom_field" cannot be resolved
+     */
+    public function testGetColumnValueWithUnknownColumn()
+    {
+        $columnResolver = $this->createColumnResolver();
+        $values = array_values($this->values);
+        $columnResolver->getColumnValue(self::CUSTOM_FIELD, $values);
+    }
+
+    /**
+     * @throws \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnNotFoundException
+     * @expectedException \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnNotFoundException
+     * @expectedExceptionMessage Column "new_custom_column" not found
+     */
+    public function testGetColumnValueWithUndefinedValue()
+    {
+        $columnName = 'new_custom_column';
+
+        $headers = array_keys($this->values);
+        $headers[] = $columnName;
+        $columnResolver = $this->createColumnResolver($headers);
+        $values = array_values($this->values);
+        $columnResolver->getColumnValue($columnName, $values);
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/RowParserTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/RowParserTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc6ec40b46e084bcd7a22807dd223e7965e54433
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/CSV/RowParserTest.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Test\Unit\Model\ResourceModel\Carrier\Tablerate\CSV;
+
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolver;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowException;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowParser;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\LocationDirectory;
+
+/**
+ * Unit test for Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowParser
+ */
+class RowParserTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var  ColumnResolver|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $columnResolverMock;
+
+    /**
+     * @var RowParser
+     */
+    private $rowParser;
+
+    /**
+     * @var LocationDirectory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $locationDirectoryMock;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function setUp()
+    {
+        $this->locationDirectoryMock = $this->getMockBuilder(LocationDirectory::class)
+            ->setMethods(['hasCountryId', 'getCountryId', 'hasRegionId', 'getRegionId'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->columnResolverMock = $this->getMockBuilder(ColumnResolver::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->rowParser = new RowParser(
+            $this->locationDirectoryMock
+        );
+    }
+
+    /**
+     * @return void
+     */
+    public function testGetColumns()
+    {
+        $columns = $this->rowParser->getColumns();
+        $this->assertTrue(is_array($columns), 'Columns should be array, ' . gettype($columns) . ' given');
+        $this->assertNotEmpty($columns);
+    }
+
+    /**
+     * @return void
+     */
+    public function testParse()
+    {
+        $expectedResult = [
+            'website_id' => 58,
+            'dest_country_id' => '0',
+            'dest_region_id' => 0,
+            'dest_zip' => '*',
+            'condition_name' => 'condition_short_name',
+            'condition_value' => 40.0,
+            'price' => 350.0,
+        ];
+        $rowData = ['a', 'b', 'c', 'd', 'e'];
+        $rowNumber = 120;
+        $websiteId = 58;
+        $conditionShortName = 'condition_short_name';
+        $conditionFullName = 'condition_full_name';
+        $columnValueMap = [
+            [ColumnResolver::COLUMN_COUNTRY, $rowData, '*'],
+            [ColumnResolver::COLUMN_REGION, $rowData, '*'],
+            [ColumnResolver::COLUMN_ZIP, $rowData, ''],
+            [$conditionFullName, $rowData, 40],
+            [ColumnResolver::COLUMN_PRICE, $rowData, 350],
+        ];
+        $result = $this->parse(
+            $rowData,
+            $conditionFullName,
+            $rowNumber,
+            $websiteId,
+            $conditionShortName,
+            $columnValueMap
+        );
+        $this->assertEquals($expectedResult, $result);
+    }
+
+    /**
+     * @param array $rowData
+     * @param $conditionFullName
+     * @param array $columnValueMap
+     * @param $expectedMessage
+     * @throws null|RowException
+     * @dataProvider parseWithExceptionDataProvider
+     * @expectedException \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowException
+     */
+    public function testParseWithException(array $rowData, $conditionFullName, array $columnValueMap, $expectedMessage)
+    {
+        $rowNumber = 120;
+        $websiteId = 58;
+        $conditionShortName = 'condition_short_name';
+        $actualMessage = null;
+        $exception = null;
+        try {
+            $this->parse(
+                $rowData,
+                $conditionFullName,
+                $rowNumber,
+                $websiteId,
+                $conditionShortName,
+                $columnValueMap
+            );
+        } catch (\Exception $e) {
+            $actualMessage = $e->getMessage();
+            $exception = $e;
+        }
+        $this->assertEquals($expectedMessage, $actualMessage);
+        throw $exception;
+    }
+
+    public function parseWithExceptionDataProvider()
+    {
+        $rowData = ['a', 'b', 'c', 'd', 'e'];
+        $conditionFullName = 'condition_full_name';
+        return [
+            [
+                $rowData,
+                $conditionFullName,
+                [
+                    [ColumnResolver::COLUMN_COUNTRY, $rowData, 'XX'],
+                    [ColumnResolver::COLUMN_REGION, $rowData, '*'],
+                    [ColumnResolver::COLUMN_ZIP, $rowData, ''],
+                    [$conditionFullName, $rowData, 40],
+                    [ColumnResolver::COLUMN_PRICE, $rowData, 350],
+                ],
+                'Please correct Country "XX" in the Row #120.',
+            ],
+            [
+                $rowData,
+                $conditionFullName,
+                [
+                    [ColumnResolver::COLUMN_COUNTRY, $rowData, '*'],
+                    [ColumnResolver::COLUMN_REGION, $rowData, 'AA'],
+                    [ColumnResolver::COLUMN_ZIP, $rowData, ''],
+                    [$conditionFullName, $rowData, 40],
+                    [ColumnResolver::COLUMN_PRICE, $rowData, 350],
+                ],
+                'Please correct Region/State "AA" in the Row #120.',
+            ],
+            [
+                $rowData,
+                $conditionFullName,
+                [
+                    [ColumnResolver::COLUMN_COUNTRY, $rowData, '*'],
+                    [ColumnResolver::COLUMN_REGION, $rowData, '*'],
+                    [ColumnResolver::COLUMN_ZIP, $rowData, ''],
+                    [$conditionFullName, $rowData, 'QQQ'],
+                    [ColumnResolver::COLUMN_PRICE, $rowData, 350],
+                ],
+                'Please correct condition_full_name "QQQ" in the Row #120.',
+            ],
+            [
+                $rowData,
+                $conditionFullName,
+                [
+                    [ColumnResolver::COLUMN_COUNTRY, $rowData, '*'],
+                    [ColumnResolver::COLUMN_REGION, $rowData, '*'],
+                    [ColumnResolver::COLUMN_ZIP, $rowData, ''],
+                    [$conditionFullName, $rowData, 40],
+                    [ColumnResolver::COLUMN_PRICE, $rowData, 'BBB'],
+                ],
+                'Please correct Shipping Price "BBB" in the Row #120.',
+            ],
+        ];
+    }
+
+    /**
+     * @param $rowData
+     * @param $conditionFullName
+     * @param $rowNumber
+     * @param $websiteId
+     * @param $conditionShortName
+     * @return array
+     * @throws \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowException
+     */
+    private function parse($rowData, $conditionFullName, $rowNumber, $websiteId, $conditionShortName, $columnValueMap)
+    {
+        $this->columnResolverMock->expects($this->any())
+            ->method('getColumnValue')
+            ->willReturnMap($columnValueMap);
+        $result = $this->rowParser->parse(
+            $rowData,
+            $rowNumber,
+            $websiteId,
+            $conditionShortName,
+            $conditionFullName,
+            $this->columnResolverMock
+        );
+        return $result;
+    }
+}
diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/ImportTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/ImportTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0bb192e3deacbdbc0e118795fa1e911d75781ac9
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/ResourceModel/Carrier/Tablerate/ImportTest.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\OfflineShipping\Test\Unit\Model\ResourceModel\Carrier\Tablerate;
+
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Filesystem\File\ReadInterface;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolverFactory;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\ColumnResolver;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\CSV\RowParser;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\DataHashGenerator;
+use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import;
+use Magento\Store\Model\StoreManagerInterface;
+
+/**
+ * Unit test for Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import
+ */
+class ImportTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import
+     */
+    private $import;
+
+    /**
+     * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeManagerMock;
+
+    /**
+     * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filesystemMock;
+
+    /**
+     * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $scopeConfigMock;
+
+    /**
+     * @var RowParser|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $rowParserMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $columnResolverFactoryMock;
+
+    /**
+     * @var DataHashGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $dataHashGeneratorMock;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function setUp()
+    {
+        $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)
+            ->getMockForAbstractClass();
+        $this->filesystemMock = $this->getMockBuilder(Filesystem::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)
+            ->getMockForAbstractClass();
+        $this->rowParserMock = $this->getMockBuilder(RowParser::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->columnResolverFactoryMock = $this->getMockBuilder(ColumnResolverFactory::class)
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->dataHashGeneratorMock = $this->getMockBuilder(DataHashGenerator::class)
+            ->getMock();
+        $this->rowParserMock->expects($this->any())
+            ->method('parse')
+            ->willReturnArgument(0);
+        $this->dataHashGeneratorMock->expects($this->any())
+            ->method('getHash')
+            ->willReturnCallback(
+                function (array $data) {
+                    return implode('_', $data);
+                }
+            );
+
+        $this->import = new Import(
+            $this->storeManagerMock,
+            $this->filesystemMock,
+            $this->scopeConfigMock,
+            $this->rowParserMock,
+            $this->columnResolverFactoryMock,
+            $this->dataHashGeneratorMock
+        );
+    }
+
+    /**
+     * @return void
+     */
+    public function testGetColumns()
+    {
+        $columns = ['column_1', 'column_2'];
+        $this->rowParserMock->expects($this->once())
+            ->method('getColumns')
+            ->willReturn($columns);
+        $result = $this->import->getColumns();
+        $this->assertEquals($columns, $result);
+    }
+
+    /**
+     * @return void
+     */
+    public function testGetData()
+    {
+        $lines = [
+            ['header_1', 'header_2', 'header_3', 'header_4', 'header_5'],
+            ['a1', 'b1', 'c1', 'd1', 'e1'],
+            ['a2', 'b2', 'c2', 'd2', 'e2'],
+            ['a3', 'b3', 'c3', 'd3', 'e3'],
+            ['a4', 'b4', 'c4', 'd4', 'e4'],
+            ['a5', 'b5', 'c5', 'd5', 'e5'],
+        ];
+        $file = $this->createFileMock($lines);
+        $expectedResult = [
+            [
+                $lines[1],
+                $lines[2],
+            ],
+            [
+                $lines[3],
+                $lines[4],
+            ],
+            [
+                $lines[5]
+            ]
+        ];
+
+        $columnResolver = $this->getMockBuilder(ColumnResolver::class)->disableOriginalConstructor()->getMock();
+        $this->columnResolverFactoryMock
+            ->expects($this->once())
+            ->method('create')
+            ->with(['headers' => $lines[0]])
+            ->willReturn($columnResolver);
+
+        $result = [];
+        foreach ($this->import->getData($file, 1, 'short_name', 'full_name', 2) as $bunch) {
+            $result[] = $bunch;
+        }
+        $this->assertEquals($expectedResult, $result);
+        $this->assertFalse($this->import->hasErrors());
+        $this->assertEquals([], $this->import->getErrors());
+    }
+
+    /**
+     * @return void
+     */
+    public function testGetDataWithDuplicatedLine()
+    {
+        $lines = [
+            ['header_1', 'header_2', 'header_3', 'header_4', 'header_5'],
+            ['a1', 'b1', 'c1', 'd1', 'e1'],
+            ['a1', 'b1', 'c1', 'd1', 'e1'],
+            [],
+            ['a2', 'b2', 'c2', 'd2', 'e2'],
+        ];
+        $file = $this->createFileMock($lines);
+        $expectedResult = [
+            [
+                $lines[1],
+                $lines[4],
+            ],
+        ];
+
+        $columnResolver = $this->getMockBuilder(ColumnResolver::class)->disableOriginalConstructor()->getMock();
+        $this->columnResolverFactoryMock
+            ->expects($this->once())
+            ->method('create')
+            ->with(['headers' => $lines[0]])
+            ->willReturn($columnResolver);
+
+        $result = [];
+        foreach ($this->import->getData($file, 1, 'short_name', 'full_name', 2) as $bunch) {
+            $result[] = $bunch;
+        }
+        $this->assertEquals($expectedResult, $result);
+        $this->assertTrue($this->import->hasErrors());
+        $this->assertEquals(['Duplicate Row #%1 (duplicates row #%2)'], $this->import->getErrors());
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Please correct Table Rates File Format.
+     * @SuppressWarnings(PHPMD.UnusedLocalVariable)
+     */
+    public function testGetDataFromEmptyFile()
+    {
+        $lines = [];
+        $file = $this->createFileMock($lines);
+        foreach ($this->import->getData($file, 1, 'short_name', 'full_name', 2) as $bunch) {
+            $this->assertTrue(false, 'Exception about empty header is not thrown');
+        }
+    }
+
+    /**
+     * @param array $lines
+     * @return ReadInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createFileMock(array $lines)
+    {
+        $file = $this->getMockBuilder(ReadInterface::class)
+            ->setMethods(['readCsv'])
+            ->getMockForAbstractClass();
+        $i = 0;
+        foreach ($lines as $line) {
+            $file->expects($this->at($i))
+                ->method('readCsv')
+                ->willReturn($line);
+            $i++;
+        }
+        $file->expects($this->at($i))
+            ->method('readCsv')
+            ->willReturn(false);
+        return $file;
+    }
+}
diff --git a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php
index fd04f1fe4ca2fbe28854f837b0a3323d70164473..a8871f803a1be31fe12bd2fef45a1ad2a3d35f84 100644
--- a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php
+++ b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php
@@ -89,8 +89,10 @@ class BuiltinPlugin
      */
     protected function addDebugHeaders(ResponseHttp $result)
     {
-        $cacheControl = $result->getHeader('Cache-Control')->getFieldValue();
-        $this->addDebugHeader($result, 'X-Magento-Cache-Control', $cacheControl);
+        $cacheControlHeader = $result->getHeader('Cache-Control');
+        if ($cacheControlHeader instanceof \Zend\Http\Header\HeaderInterface) {
+            $this->addDebugHeader($result, 'X-Magento-Cache-Control', $cacheControlHeader->getFieldValue());
+        }
         $this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'MISS', true);
         return $result;
     }
diff --git a/app/code/Magento/PageCache/Model/Cache/Server.php b/app/code/Magento/PageCache/Model/Cache/Server.php
index 2e2f3a87fb8c07a7eb33c9b61f4ef421992e6c49..797249c98cb09e61b2e14e955116d6c84f1cf83a 100644
--- a/app/code/Magento/PageCache/Model/Cache/Server.php
+++ b/app/code/Magento/PageCache/Model/Cache/Server.php
@@ -5,16 +5,17 @@
  */
 namespace Magento\PageCache\Model\Cache;
 
-use Zend\Uri\Uri;
+use Magento\Framework\UrlInterface;
 use Magento\Framework\App\DeploymentConfig;
 use Magento\Framework\Config\ConfigOptionsListConstants;
 use Magento\Framework\App\RequestInterface;
+use Zend\Uri\Uri;
 use Zend\Uri\UriFactory;
 
 class Server
 {
     /**
-     * @var \Magento\Framework\UrlInterface
+     * @var UrlInterface
      */
     protected $urlBuilder;
 
@@ -33,12 +34,12 @@ class Server
     /**
      * Constructor
      *
-     * @param \Magento\Framework\UrlInterface $urlBuilder
+     * @param UrlInterface $urlBuilder
      * @param DeploymentConfig $config
      * @param RequestInterface $request
      */
     public function __construct(
-        \Magento\Framework\UrlInterface $urlBuilder,
+        UrlInterface $urlBuilder,
         DeploymentConfig $config,
         RequestInterface $request
     ) {
@@ -56,21 +57,24 @@ class Server
     {
         $servers = [];
         $configuredHosts = $this->config->get(ConfigOptionsListConstants::CONFIG_PATH_CACHE_HOSTS);
-        if (null == $configuredHosts) {
-            $httpHost = $this->request->getHttpHost();
-            $servers[] = $httpHost ?
-                UriFactory::factory('')->setHost($httpHost)->setPort(self::DEFAULT_PORT)->setScheme('http') :
-                UriFactory::factory($this->urlBuilder->getUrl('*', ['_nosid' => true])) // Don't use SID in building URL
-                    ->setScheme('http')
-                    ->setPath(null)
-                    ->setQuery(null);
 
-        } else {
+        if (is_array($configuredHosts)) {
             foreach ($configuredHosts as $host) {
-                $servers[] = UriFactory::factory('')->setHost($host['host'])
+                $servers[] = UriFactory::factory('')
+                    ->setHost($host['host'])
                     ->setPort(isset($host['port']) ? $host['port'] : self::DEFAULT_PORT)
-                    ->setScheme('http');
+                ;
             }
+        } elseif ($this->request->getHttpHost()) {
+            $servers[] = UriFactory::factory('')->setHost($this->request->getHttpHost())->setPort(self::DEFAULT_PORT);
+        } else {
+            $servers[] = UriFactory::factory($this->urlBuilder->getUrl('*', ['_nosid' => true]));
+        }
+
+        foreach (array_keys($servers) as $key) {
+            $servers[$key]->setScheme('http')
+                ->setPath('/')
+                ->setQuery(null);
         }
         return $servers;
     }
diff --git a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
index 022824faafbd41faf61b1539f2939ee0ba0277bd..f72a0e3bc46b3a2b13ee869df164d29c439e47bd 100644
--- a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
+++ b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
@@ -73,8 +73,10 @@ class BuiltinPlugin
         }
 
         if ($this->state->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) {
-            $cacheControl = $response->getHeader('Cache-Control')->getFieldValue();
-            $response->setHeader('X-Magento-Cache-Control', $cacheControl);
+            $cacheControlHeader = $response->getHeader('Cache-Control');
+            if ($cacheControlHeader instanceof \Zend\Http\Header\HeaderInterface) {
+                $response->setHeader('X-Magento-Cache-Control', $cacheControlHeader->getFieldValue());
+            }
             $response->setHeader('X-Magento-Cache-Debug', 'MISS', true);
         }
 
diff --git a/app/code/Magento/PageCache/Observer/FlushAllCache.php b/app/code/Magento/PageCache/Observer/FlushAllCache.php
index a704c5e116bcb9df49849baf615f8671f61f0f3b..712cc51005569a953646ba69810f05e42a63e3d7 100644
--- a/app/code/Magento/PageCache/Observer/FlushAllCache.php
+++ b/app/code/Magento/PageCache/Observer/FlushAllCache.php
@@ -6,12 +6,15 @@
  */
 namespace Magento\PageCache\Observer;
 
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Event\ObserverInterface;
 
 class FlushAllCache implements ObserverInterface
 {
     /**
      * @var \Magento\Framework\App\PageCache\Cache
+     *
+     * @deprecated
      */
     protected $_cache;
 
@@ -22,6 +25,11 @@ class FlushAllCache implements ObserverInterface
      */
     protected $_config;
 
+    /**
+     * @var \Magento\PageCache\Model\Cache\Type
+     */
+    private $fullPageCache;
+
     /**
      * @param \Magento\PageCache\Model\Config $config
      * @param \Magento\Framework\App\PageCache\Cache $cache
@@ -41,7 +49,20 @@ class FlushAllCache implements ObserverInterface
     public function execute(\Magento\Framework\Event\Observer $observer)
     {
         if ($this->_config->getType() == \Magento\PageCache\Model\Config::BUILT_IN) {
-            $this->_cache->clean();
+            $this->getCache()->clean();
+        }
+    }
+
+    /**
+     * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547
+     *
+     * @return \Magento\PageCache\Model\Cache\Type
+     */
+    private function getCache()
+    {
+        if (!$this->fullPageCache) {
+            $this->fullPageCache = ObjectManager::getInstance()->get('\Magento\PageCache\Model\Cache\Type');
         }
+        return $this->fullPageCache;
     }
 }
diff --git a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php
index c77cacb77cce35648dab065025d9d147a93d9721..116162c5047b5491a424b55e9125b2ed99e21434 100644
--- a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php
+++ b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php
@@ -6,12 +6,15 @@
  */
 namespace Magento\PageCache\Observer;
 
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Event\ObserverInterface;
 
 class FlushCacheByTags implements ObserverInterface
 {
     /**
      * @var \Magento\Framework\App\PageCache\Cache
+     *
+     * @deprecated
      */
     protected $_cache;
 
@@ -22,6 +25,11 @@ class FlushCacheByTags implements ObserverInterface
      */
     protected $_config;
 
+    /**
+     * @var \Magento\PageCache\Model\Cache\Type
+     */
+    private $fullPageCache;
+
     /**
      * @param \Magento\PageCache\Model\Config $config
      * @param \Magento\Framework\App\PageCache\Cache $cache
@@ -49,9 +57,22 @@ class FlushCacheByTags implements ObserverInterface
                     $tags[] = preg_replace("~_\\d+$~", '', $tag);
                 }
                 if (!empty($tags)) {
-                    $this->_cache->clean(array_unique($tags));
+                    $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_ALL, array_unique($tags));
                 }
             }
         }
     }
+
+    /**
+     * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547
+     *
+     * @return \Magento\PageCache\Model\Cache\Type
+     */
+    private function getCache()
+    {
+        if (!$this->fullPageCache) {
+            $this->fullPageCache = ObjectManager::getInstance()->get('\Magento\PageCache\Model\Cache\Type');
+        }
+        return $this->fullPageCache;
+    }
 }
diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php
index f18c5e04468da59169ae6b3b5642d428b7113667..82d6a10598fa7f193138fa0b352f100fbf6f75ac 100644
--- a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php
@@ -5,11 +5,13 @@
  */
 namespace Magento\PageCache\Test\Unit\Model\Cache;
 
+use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use \Magento\PageCache\Model\Cache\Server;
 use \Zend\Uri\UriFactory;
 
 class ServerTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var \Magento\PageCache\Model\Cache\Server */
+    /** @var Server */
     protected $model;
 
     /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\DeploymentConfig */
@@ -30,7 +32,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
-        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $objectManager = new ObjectManager($this);
         $this->model = $objectManager->getObject(
             'Magento\PageCache\Model\Cache\Server',
             [
@@ -56,12 +58,9 @@ class ServerTest extends \PHPUnit_Framework_TestCase
         $url,
         $hostConfig = null
     ) {
-        $this->configMock->expects($this->once())
-            ->method('get')
-            ->willReturn($hostConfig);
-        $this->requestMock->expects($this->exactly($getHttpHostCallCtr))
-            ->method('getHttpHost')
-            ->willReturn($httpHost);
+        $this->configMock->expects($this->once())->method('get')->willReturn($hostConfig);
+        $this->requestMock->expects($this->exactly($getHttpHostCallCtr))->method('getHttpHost')->willReturn($httpHost);
+
         $this->urlBuilderMock->expects($this->exactly($getUrlCallCtr))
             ->method('getUrl')
             ->with('*', ['_nosid' => true])
@@ -70,30 +69,32 @@ class ServerTest extends \PHPUnit_Framework_TestCase
         $uris = [];
         if (null === $hostConfig) {
             if (!empty($httpHost)) {
-                $uris[] = UriFactory::factory('')->setHost($httpHost)
-                    ->setPort(\Magento\PageCache\Model\Cache\Server::DEFAULT_PORT)
-                    ->setScheme('http');
+                $uris[] = UriFactory::factory('')->setHost($httpHost)->setPort(Server::DEFAULT_PORT);
             }
             if (!empty($url)) {
                 $uris[] = UriFactory::factory($url);
             }
         } else {
             foreach ($hostConfig as $host) {
-                $port = isset($host['port']) ? $host['port'] : \Magento\PageCache\Model\Cache\Server::DEFAULT_PORT;
-                $uris[] = UriFactory::factory('')->setHost($host['host'])
-                    ->setPort($port)
-                    ->setScheme('http');
+                $port = isset($host['port']) ? $host['port'] : Server::DEFAULT_PORT;
+                $uris[] = UriFactory::factory('')->setHost($host['host'])->setPort($port);
             }
         }
 
+        foreach (array_keys($uris) as $key) {
+            $uris[$key]->setScheme('http')
+                ->setPath('/')
+                ->setQuery(null);
+        }
+
         $this->assertEquals($uris, $this->model->getUris());
     }
 
     public function getUrisDataProvider()
     {
         return [
-            'http host' => [1, '127.0.0.1', 0, '',],
-            'url' => [1, '', 1, 'http://host',],
+            'http host' => [2, '127.0.0.1', 0, ''],
+            'url' => [1, '', 1, 'http://host'],
             'config' => [
                 0,
                 '',
diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php
index 1ea42d6e592be247a64e22456962f030762a627f..20706ef5b790b42db45fcae76b5bcb5e728d01fe 100644
--- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php
@@ -12,18 +12,19 @@ namespace Magento\PageCache\Test\Unit\Observer;
 class FlushAllCacheTest extends \PHPUnit_Framework_TestCase
 {
     /** @var \Magento\PageCache\Observer\FlushAllCache */
-    protected $_model;
+    private $_model;
 
     /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */
-    protected $_configMock;
+    private $_configMock;
 
     /** @var  \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */
-    protected $_cacheMock;
+    private $_cacheMock;
 
-    /**
-     * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject|
-     */
-    protected $observerMock;
+    /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Event\Observer */
+    private $observerMock;
+
+    /** @var  \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */
+    private $fullPageCacheMock;
 
     /**
      * Set up all mocks and data for test
@@ -38,13 +39,18 @@ class FlushAllCacheTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->_cacheMock = $this->getMock('Magento\Framework\App\PageCache\Cache', ['clean'], [], '', false);
-
+        $this->fullPageCacheMock = $this->getMock('\Magento\PageCache\Model\Cache\Type', ['clean'], [], '', false);
         $this->observerMock = $this->getMock('Magento\Framework\Event\Observer');
 
         $this->_model = new \Magento\PageCache\Observer\FlushAllCache(
             $this->_configMock,
             $this->_cacheMock
         );
+
+        $reflection = new \ReflectionClass('\Magento\PageCache\Observer\FlushAllCache');
+        $reflectionProperty = $reflection->getProperty('fullPageCache');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock);
     }
 
     /**
@@ -60,7 +66,7 @@ class FlushAllCacheTest extends \PHPUnit_Framework_TestCase
                 $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN)
             );
 
-        $this->_cacheMock->expects($this->once())->method('clean');
+        $this->fullPageCacheMock->expects($this->once())->method('clean');
         $this->_model->execute($this->observerMock);
     }
 }
diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php
index 4020e4c9e3f9ff5e424acfad4d437ee8ec8d7176..a34dc5543c1593cacb08d2f1dc7dc738c8c938a3 100644
--- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php
@@ -20,6 +20,9 @@ class FlushCacheByTagsTest extends \PHPUnit_Framework_TestCase
     /** @var  \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */
     protected $_cacheMock;
 
+    /** @var  \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */
+    private $fullPageCacheMock;
+
     /**
      * Set up all mocks and data for test
      */
@@ -33,11 +36,16 @@ class FlushCacheByTagsTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->_cacheMock = $this->getMock('Magento\Framework\App\PageCache\Cache', ['clean'], [], '', false);
+        $this->fullPageCacheMock = $this->getMock('\Magento\PageCache\Model\Cache\Type', ['clean'], [], '', false);
 
         $this->_model = new \Magento\PageCache\Observer\FlushCacheByTags(
             $this->_configMock,
             $this->_cacheMock
         );
+        $reflection = new \ReflectionClass('\Magento\PageCache\Observer\FlushCacheByTags');
+        $reflectionProperty = $reflection->getProperty('fullPageCache');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock);
     }
 
     /**
@@ -59,16 +67,14 @@ class FlushCacheByTagsTest extends \PHPUnit_Framework_TestCase
             $eventMock = $this->getMock('Magento\Framework\Event', ['getObject'], [], '', false);
             $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($observedObject));
             $observerObject->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock));
-            $this->_configMock->expects(
-                $this->once()
-            )->method(
-                    'getType'
-                )->will(
-                    $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN)
-                );
+            $this->_configMock->expects($this->once())
+                ->method('getType')
+                ->willReturn(\Magento\PageCache\Model\Config::BUILT_IN);
             $observedObject->expects($this->once())->method('getIdentities')->will($this->returnValue($tags));
 
-            $this->_cacheMock->expects($this->once())->method('clean')->with($this->equalTo($expectedTags));
+            $this->fullPageCacheMock->expects($this->once())
+                ->method('clean')
+                ->with(\Zend_Cache::CLEANING_MODE_ALL, $this->equalTo($expectedTags));
         }
 
         $this->_model->execute($observerObject);
@@ -102,7 +108,7 @@ class FlushCacheByTagsTest extends \PHPUnit_Framework_TestCase
         );
         $observedObject->expects($this->once())->method('getIdentities')->will($this->returnValue($tags));
 
-        $this->_cacheMock->expects($this->never())->method('clean');
+        $this->fullPageCacheMock->expects($this->never())->method('clean');
 
         $this->_model->execute($observerObject);
     }
diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Field/Hidden.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Field/Hidden.php
index 1a5e6618d3f36a3138208d1faca614d57e43ec92..d8a23f08db80e15b9cf7770e47d1391972aec1e4 100644
--- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Field/Hidden.php
+++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Field/Hidden.php
@@ -18,7 +18,7 @@ class Hidden extends \Magento\Config\Block\System\Config\Form\Field
      * @param string $html
      * @return string
      */
-    protected function _decorateRowHtml($element, $html)
+    protected function _decorateRowHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element, $html)
     {
         return '<tr id="row_' . $element->getHtmlId() . '" style="display: none;">' . $html . '</tr>';
     }
diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Grid/Column/Renderer/Date.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Grid/Column/Renderer/Date.php
index 7ffe3a73a943da66043c958b92be6f53c1cb1b3a..e763d77f3b4803d153911af7aacf372eed6d0965 100644
--- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Grid/Column/Renderer/Date.php
+++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Grid/Column/Renderer/Date.php
@@ -13,6 +13,12 @@ use Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface;
  */
 class Date extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Date
 {
+
+    /**
+     * @var \Magento\Framework\Locale\ResolverInterface
+     */
+    private $localeResolver;
+
     /**
      * Constructor
      *
@@ -28,7 +34,7 @@ class Date extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Date
         array $data = []
     ) {
         parent::__construct($context, $dateTimeFormatter, $data);
-        $this->_localeResolver = $localeResolver;
+        $this->localeResolver = $localeResolver;
     }
 
     /**
@@ -41,7 +47,7 @@ class Date extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Date
         $format = $this->getColumn()->getFormat();
         if (!$format) {
             $dataBundle = new DataBundle();
-            $resourceBundle = $dataBundle->get($this->_localeResolver->getLocale());
+            $resourceBundle = $dataBundle->get($this->localeResolver->getLocale());
             $formats = $resourceBundle['calendar']['gregorian']['availableFormats'];
             switch ($this->getColumn()->getPeriodType()) {
                 case 'month':
@@ -81,7 +87,7 @@ class Date extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Date
             } else {
                 $date = $this->_localeDate->date(new \DateTime($data), null, false);
             }
-            return $this->dateTimeFormatter->formatObject($date, $format, $this->_localeResolver->getLocale());
+            return $this->dateTimeFormatter->formatObject($date, $format, $this->localeResolver->getLocale());
         }
         return $this->getColumn()->getDefault();
     }
diff --git a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js
index 19e25f16a442f352dbb3df185ee39511d8f5d08e..b55d20968db6923bab8e796e60d3b17b486420b8 100644
--- a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js
+++ b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js
@@ -10,6 +10,7 @@ define([
     function processReviews(url, fromPages) {
         $.ajax({
             url: url,
+            cache: true,
             dataType: 'html'
         }).done(function (data) {
             $('#product-review-container').html(data);
diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items.php b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items.php
index 326fb104bbb2c242679fccb91bcbc16fe0ef515e..0f84c6a4f0205bd9c41b58e86f81561e41b2183e 100644
--- a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items.php
+++ b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items.php
@@ -12,6 +12,15 @@ use Magento\Sales\Model\ResourceModel\Order\Item\Collection;
  */
 class Items extends \Magento\Sales\Block\Adminhtml\Items\AbstractItems
 {
+    /**
+     * @return array
+     */
+    public function getColumns()
+    {
+        $columns = array_key_exists('columns', $this->_data) ? $this->_data['columns'] : [];
+        return $columns;
+    }
+
     /**
      * Retrieve required options from parent
      *
diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items/Renderer/DefaultRenderer.php b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items/Renderer/DefaultRenderer.php
index 30f059d0dbfe3c4e390469e9a7a4499e65feaa00..c22d0e7b55fbb691a5852203be5e78af703cb676 100644
--- a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items/Renderer/DefaultRenderer.php
+++ b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Items/Renderer/DefaultRenderer.php
@@ -254,4 +254,57 @@ class DefaultRenderer extends \Magento\Sales\Block\Adminhtml\Items\Renderer\Defa
             $this->_checkoutHelper->getPriceInclTax($item)
         );
     }
+
+    /**
+     * @param \Magento\Framework\DataObject|Item $item
+     * @param string $column
+     * @param null $field
+     * @return string
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    public function getColumnHtml(\Magento\Framework\DataObject $item, $column, $field = null)
+    {
+        $html = '';
+        switch ($column) {
+            case 'product':
+                if ($this->canDisplayContainer()) {
+                    $html .= '<div id="' . $this->getHtmlId() . '">';
+                }
+                $html .= $this->getColumnHtml($item, 'name');
+                if ($this->canDisplayContainer()) {
+                    $html .= '</div>';
+                }
+                break;
+            case 'status':
+                $html = $item->getStatus();
+                break;
+            case 'price-original':
+                $html = $this->displayPriceAttribute('original_price');
+                break;
+            case 'price':
+                $html = $this->displayPriceAttribute('price');
+                break;
+            case 'tax-amount':
+                $html = $this->displayPriceAttribute('tax_amount');
+                break;
+            case 'tax-percent':
+                $html = $this->displayTaxPercent($item);
+                break;
+            case 'discont':
+                $html = $this->displayPriceAttribute('discount_amount');
+                break;
+            default:
+                $html = parent::getColumnHtml($item, $column, $field);
+        }
+        return $html;
+    }
+
+    /**
+     * @return array
+     */
+    public function getColumns()
+    {
+        $columns = array_key_exists('columns', $this->_data) ? $this->_data['columns'] : [];
+        return $columns;
+    }
 }
diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php
index d32aed76cb78b0efb7f17bfd5898317dec6cddda..8c0a31d790c966af5a8af7db35d699b8eb3d054e 100644
--- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php
+++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php
@@ -127,13 +127,14 @@ class OrderSender extends Sender
             'formattedShippingAddress' => $this->getFormattedShippingAddress($order),
             'formattedBillingAddress' => $this->getFormattedBillingAddress($order),
         ];
+        $transport = new \Magento\Framework\DataObject($transport);
 
         $this->eventManager->dispatch(
             'email_order_set_template_vars_before',
             ['sender' => $this, 'transport' => $transport]
         );
 
-        $this->templateContainer->setTemplateVars($transport);
+        $this->templateContainer->setTemplateVars($transport->getData());
 
         parent::prepareTemplate($order);
     }
diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_view.xml
index 10915f10d9355c4da3401dcab6fc330fe71b76dd..4e98f3b893d90505e4531409744c89e4d914e46b 100644
--- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_view.xml
+++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_view.xml
@@ -24,7 +24,36 @@
                     <block class="Magento\Sales\Block\Adminhtml\Order\View\Messages" name="order_messages"/>
                     <block class="Magento\Sales\Block\Adminhtml\Order\View\Info" name="order_info" template="order/view/info.phtml"/>
                     <block class="Magento\Sales\Block\Adminhtml\Order\View\Items" name="order_items" template="order/view/items.phtml">
-                        <block class="Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer" as="default" template="order/view/items/renderer/default.phtml"/>
+                        <arguments>
+                            <argument name="columns" xsi:type="array">
+                                <item name="product" xsi:type="string" translate="true">Product</item>
+                                <item name="status" xsi:type="string" translate="true">Item Status</item>
+                                <item name="price-original" xsi:type="string" translate="true">Original Price</item>
+                                <item name="price" xsi:type="string" translate="true">Price</item>
+                                <item name="ordered-qty" xsi:type="string" translate="true">Qty</item>
+                                <item name="subtotal" xsi:type="string" translate="true">Subtotal</item>
+                                <item name="tax-amount" xsi:type="string" translate="true">Tax Amount</item>
+                                <item name="tax-percent" xsi:type="string" translate="true">Tax Percent</item>
+                                <item name="discont" xsi:type="string" translate="true">Discount Amount</item>
+                                <item name="total" xsi:type="string" translate="true">Row Total</item>
+                            </argument>
+                        </arguments>
+                        <block class="Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer" as="default" template="order/view/items/renderer/default.phtml">
+                        <arguments>
+                            <argument name="columns" xsi:type="array">
+                                <item name="product" xsi:type="string" translate="false">col-product</item>
+                                <item name="status" xsi:type="string" translate="false">col-status</item>
+                                <item name="price-original" xsi:type="string" translate="false">col-price-original</item>
+                                <item name="price" xsi:type="string" translate="false">col-price</item>
+                                <item name="qty" xsi:type="string" translate="false">col-ordered-qty</item>
+                                <item name="subtotal" xsi:type="string" translate="false">col-subtotal</item>
+                                <item name="tax-amount" xsi:type="string" translate="false">col-tax-amount</item>
+                                <item name="tax-percent" xsi:type="string" translate="false">col-tax-percent</item>
+                                <item name="discont" xsi:type="string" translate="false">col-discont</item>
+                                <item name="total" xsi:type="string" translate="false">col-total</item>
+                            </argument>
+                        </arguments>
+                        </block>
                         <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="items/column/qty.phtml" group="column"/>
                         <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="items/column/name.phtml" group="column"/>
                         <block class="Magento\Framework\View\Element\Text\ListText" name="order_item_extra_info"/>
diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/items.phtml
index 879872ee9271a7bcb883f5ff2b84c5687fe8a5e5..41cb768997bfba9ff1532c50bdf32cefa7f5d864 100644
--- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/items.phtml
+++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/items.phtml
@@ -5,23 +5,23 @@
  */
 
 // @codingStandardsIgnoreFile
-
 ?>
-<?php $_order = $block->getOrder() ?>
+<?php
+/**
+ * @var \Magento\Sales\Block\Adminhtml\Order\View\Items $block
+ */
+$_order = $block->getOrder() ?>
 <div class="admin__table-wrapper">
     <table class="data-table admin__table-primary edit-order-table">
         <thead>
             <tr class="headings">
-                <th class="col-product"><span><?php /* @escapeNotVerified */ echo __('Product') ?></span></th>
-                <th class="col-status"><span><?php /* @escapeNotVerified */ echo __('Item Status') ?></span></th>
-                <th class="col-price-original"><span><?php /* @escapeNotVerified */ echo __('Original Price') ?></span></th>
-                <th class="col-price"><span><?php /* @escapeNotVerified */ echo __('Price') ?></span></th>
-                <th class="col-ordered-qty"><span><?php /* @escapeNotVerified */ echo __('Qty') ?></span></th>
-                <th class="col-subtotal"><span><?php /* @escapeNotVerified */ echo __('Subtotal') ?></span></th>
-                <th class="col-tax-amount"><span><?php /* @escapeNotVerified */ echo __('Tax Amount') ?></span></th>
-                <th class="col-tax-percent"><span><?php /* @escapeNotVerified */ echo __('Tax Percent') ?></span></th>
-                <th class="col-discont"><span><?php /* @escapeNotVerified */ echo __('Discount Amount') ?></span></th>
-                <th class="col-total last"><span><?php /* @escapeNotVerified */ echo __('Row Total') ?></span></th>
+                <?php $i = 0;
+                $columns = $block->getColumns();
+                $lastItemNumber = count($columns) ?>
+                <?php foreach ($columns as $columnName => $columnTitle):?>
+                    <?php $i++; ?>
+                    <th class="col-<?php /* @noEscape */ echo $columnName ?><?php /* @noEscape */ echo ($i === $lastItemNumber ? ' last' : '')?>"><span><?php /* @noEscape */ echo $columnTitle ?></span></th>
+                <?php endforeach; ?>
             </tr>
         </thead>
         <?php $_items = $block->getItemsCollection();?>
@@ -31,7 +31,7 @@
             } else {
                 $i++;
             }?>
-            <tbody class="<?php /* @escapeNotVerified */ echo $i%2 ? 'even' : 'odd' ?>">
+            <tbody class="<?php /* @noEscape */ echo $i%2 ? 'even' : 'odd' ?>">
                 <?php echo $block->getItemHtml($_item) ?>
                 <?php echo $block->getItemExtraInfoHtml($_item) ?>
             </tbody>
diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/items/renderer/default.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/items/renderer/default.phtml
index da3367344c439d6deabd167a801bcc80e5937fa2..aec3fc17b54125b97f86e6ee7fc78498535b4db8 100644
--- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/items/renderer/default.phtml
+++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/items/renderer/default.phtml
@@ -7,33 +7,15 @@
 // @codingStandardsIgnoreFile
 
 ?>
-<?php /** @var $block \Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer */ ?>
+<?php /** @var \Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer $block */ ?>
 <?php $_item = $block->getItem() ?>
 <?php $block->setPriceDataObject($_item) ?>
 <tr>
-    <td class="col-product">
-        <?php if ($block->canDisplayContainer()): ?>
-        <div id="<?php echo $block->getHtmlId() ?>">
-        <?php endif; ?>
-        <?php echo $block->getColumnHtml($_item, 'name') ?>
-        <?php if ($block->canDisplayContainer()): ?>
-        </div>
-        <?php endif ?>
-    </td>
-    <td class="col-status"><?php /* @escapeNotVerified */ echo $_item->getStatus() ?></td>
-    <td class="col-price-original"><?php /* @escapeNotVerified */ echo $block->displayPriceAttribute('original_price') ?></td>
-    <td class="col-price">
-        <?php echo $block->getColumnHtml($_item, 'price'); ?>
-    </td>
-    <td class="col-ordered-qty"><?php echo $block->getColumnHtml($_item, 'qty') ?></td>
-
-    <td class="col-subtotal">
-        <?php echo $block->getColumnHtml($_item, 'subtotal'); ?>
-    </td>
-    <td class="col-tax-amount"><?php /* @escapeNotVerified */ echo $block->displayPriceAttribute('tax_amount') ?></td>
-    <td class="col-tax-percent"><?php /* @escapeNotVerified */ echo $block->displayTaxPercent($_item) ?></td>
-    <td class="col-discont"><?php /* @escapeNotVerified */ echo $block->displayPriceAttribute('discount_amount') ?></td>
-    <td class="col-total last">
-        <?php echo $block->getColumnHtml($_item, 'total'); ?>
-    </td>
+    <?php $i = 0;
+    $columns = $block->getColumns();
+    $lastItemNumber = count($columns) ?>
+    <?php foreach ($columns as $columnName => $columnClass):?>
+        <?php $i++; ?>
+        <td class="<?php echo /* @noEscape */ $columnClass?><?php /* @noEscape */ echo ($i === $lastItemNumber ? ' last' : '')?>"><?php /* @escapeNotVerified */ echo $block->getColumnHtml($_item, $columnName) ?></td>
+    <?php endforeach; ?>
 </tr>
diff --git a/app/code/Magento/Sales/view/frontend/email/order_new.html b/app/code/Magento/Sales/view/frontend/email/order_new.html
index 8c7bbf3302938959d1c1cb62649914e9a77b1202..5963b574830c5718c0d1adbf7632aaac53a8ff1a 100644
--- a/app/code/Magento/Sales/view/frontend/email/order_new.html
+++ b/app/code/Magento/Sales/view/frontend/email/order_new.html
@@ -13,6 +13,7 @@
 "var payment_html|raw":"Payment Details",
 "var formattedShippingAddress|raw":"Shipping Address",
 "var order.getShippingDescription()":"Shipping Description"
+"var shipping_msg":"Shipping message"
 } @-->
 
 {{template config_path="design/email/header_template"}}
@@ -73,6 +74,9 @@
                     <td class="method-info">
                         <h3>{{trans "Shipping Method"}}</h3>
                         <p>{{var order.getShippingDescription()}}</p>
+                        {{if shipping_msg}}
+                        <p>{{var shipping_msg}}</p>
+                        {{/if}}
                     </td>
                     {{/depend}}
                 </tr>
diff --git a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html
index 42bd401c7ae6651b9214959ee53c04011f422c13..4669fc43ff077cf241512343db8cdb2a4afc8f66 100644
--- a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html
+++ b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html
@@ -15,6 +15,7 @@
 "var payment_html|raw":"Payment Details",
 "var formattedShippingAddress|raw":"Shipping Address",
 "var order.getShippingDescription()":"Shipping Description"
+"var shipping_msg":"Shipping message"
 } @-->
 {{template config_path="design/email/header_template"}}
 
@@ -71,6 +72,9 @@
                     <td class="method-info">
                         <h3>{{trans "Shipping Method"}}</h3>
                         <p>{{var order.getShippingDescription()}}</p>
+                        {{if shipping_msg}}
+                        <p>{{var shipping_msg}}</p>
+                        {{/if}}
                     </td>
                     {{/depend}}
                 </tr>
diff --git a/app/code/Magento/Security/Controller/Adminhtml/Session/Check.php b/app/code/Magento/Security/Controller/Adminhtml/Session/Check.php
index e14a2b8f7ffb51684eb4f42b8e6beec81f0029d2..bdf990f05f619119ee7e474ea5d99c2003ea818e 100644
--- a/app/code/Magento/Security/Controller/Adminhtml/Session/Check.php
+++ b/app/code/Magento/Security/Controller/Adminhtml/Session/Check.php
@@ -7,6 +7,7 @@ namespace Magento\Security\Controller\Adminhtml\Session;
 
 use Magento\Backend\App\Action\Context;
 use Magento\Framework\Controller\Result\JsonFactory;
+use Magento\Security\Helper\SecurityCookie;
 use Magento\Security\Model\AdminSessionsManager;
 
 /**
@@ -17,15 +18,14 @@ class Check extends \Magento\Backend\App\Action
     /**
      * @var JsonFactory
      */
-    protected $jsonFactory;
+    private $jsonFactory;
 
     /**
      * @var AdminSessionsManager
      */
-    protected $sessionsManager;
+    private $sessionsManager;
 
     /**
-     * Check constructor.
      * @param Context $context
      * @param JsonFactory $jsonFactory
      * @param AdminSessionsManager $sessionsManager
@@ -48,7 +48,7 @@ class Check extends \Magento\Backend\App\Action
         /** @var \Magento\Framework\Controller\Result\Json $resultJson */
         return $this->jsonFactory->create()->setData(
             [
-                'isActive' => $this->sessionsManager->getCurrentSession()->isActive()
+                'isActive' => $this->sessionsManager->getCurrentSession()->isLoggedInStatus()
             ]
         );
     }
diff --git a/app/code/Magento/Security/Model/AdminSessionInfo.php b/app/code/Magento/Security/Model/AdminSessionInfo.php
index d47ae28dc4224b38d477f2fa80517699307b70b2..87b9e3ca0f17a855eb49ffcc94a75cd363404bab 100644
--- a/app/code/Magento/Security/Model/AdminSessionInfo.php
+++ b/app/code/Magento/Security/Model/AdminSessionInfo.php
@@ -89,17 +89,20 @@ class AdminSessionInfo extends \Magento\Framework\Model\AbstractModel
      */
     public function isLoggedInStatus()
     {
+        $this->checkActivity();
         return $this->getData('status') == self::LOGGED_IN;
     }
 
     /**
-     * Check if a user is active
+     * Check if session is timed out and set status accordingly
      *
-     * @return bool
+     * @return void
      */
-    public function isActive()
+    private function checkActivity()
     {
-        return $this->isLoggedInStatus() && !$this->isSessionExpired();
+        if ($this->isSessionExpired()) {
+            $this->setData('status', self::LOGGED_OUT);
+        }
     }
 
     /**
diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php
index 77c057f7a63f8d23e7440d7126e318f8b598f690..29249ee9fb350aff6d8da260c191a732457b31e6 100644
--- a/app/code/Magento/Security/Model/AdminSessionsManager.php
+++ b/app/code/Magento/Security/Model/AdminSessionsManager.php
@@ -147,7 +147,7 @@ class AdminSessionsManager
     {
         switch ((int)$statusCode) {
             case AdminSessionInfo::LOGGED_IN:
-                $reasonMessage = '';
+                $reasonMessage = null;
                 break;
             case AdminSessionInfo::LOGGED_OUT_BY_LOGIN:
                 $reasonMessage = __(
diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php
index 1ee9aa5cc9ba0d673009918308984454e71eb91b..ea15828afc733e135bfc16f7325dad2fdac21ff2 100644
--- a/app/code/Magento/Security/Model/Plugin/AuthSession.php
+++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php
@@ -7,7 +7,6 @@ namespace Magento\Security\Model\Plugin;
 
 use Magento\Backend\Model\Auth\Session;
 use Magento\Security\Model\AdminSessionsManager;
-use Magento\Framework\Stdlib\Cookie\CookieReaderInterface;
 
 /**
  * Magento\Backend\Model\Auth\Session decorator
@@ -62,7 +61,7 @@ class AuthSession
     public function aroundProlong(Session $session, \Closure $proceed)
     {
         if (!$this->isSessionCheckRequest()) {
-            if (!$this->sessionsManager->getCurrentSession()->isActive()) {
+            if (!$this->sessionsManager->getCurrentSession()->isLoggedInStatus()) {
                 $session->destroy();
                 $this->addUserLogoutNotification();
                 return null;
@@ -84,10 +83,8 @@ class AuthSession
             $this->securityCookieHelper->setLogoutReasonCookie(
                 $this->sessionsManager->getCurrentSession()->getStatus()
             );
-        } else {
-            $this->messageManager->addError(
-                $this->sessionsManager->getLogoutReasonMessage()
-            );
+        } else if ($message = $this->sessionsManager->getLogoutReasonMessage()) {
+            $this->messageManager->addError($message);
         }
 
         return $this;
diff --git a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/CheckTest.php b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/CheckTest.php
index 959c7e62a63c1a99f3d60472278f432429c38dcc..03bc6029a9669a7d398db75207ffcb3473853cc9 100644
--- a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/CheckTest.php
+++ b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/CheckTest.php
@@ -64,7 +64,7 @@ class CheckTest extends \PHPUnit_Framework_TestCase
 
         $this->currentSession =  $this->getMock(
             '\Magento\Security\Model\AdminSessionInfo',
-            ['isActive'],
+            ['isLoggedInStatus'],
             [],
             '',
             false
@@ -92,7 +92,7 @@ class CheckTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
         $this->sessionsManager->expects($this->any())->method('getCurrentSession')->willReturn($this->currentSession);
-        $this->currentSession->expects($this->any())->method('isActive')
+        $this->currentSession->expects($this->any())->method('isLoggedInStatus')
             ->will($this->returnValue($result));
         $this->jsonFactory->expects($this->any())->method('create')->willReturn($jsonMock);
         $jsonMock->expects($this->once())
diff --git a/app/code/Magento/Security/Test/Unit/Model/AdminSessionInfoTest.php b/app/code/Magento/Security/Test/Unit/Model/AdminSessionInfoTest.php
index 8d1e5569cb28b46ec16cb33095e5c748f3116516..efbae38d58d2a0edf6a9504b1fdbf7e3edc7a31c 100644
--- a/app/code/Magento/Security/Test/Unit/Model/AdminSessionInfoTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/AdminSessionInfoTest.php
@@ -19,7 +19,7 @@ class AdminSessionInfoTest extends \PHPUnit_Framework_TestCase
     protected $model;
 
     /**
-     * @var \Magento\Security\Helper\SecurityConfig
+     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Security\Helper\SecurityConfig
      */
     protected $securityConfigMock;
 
@@ -57,9 +57,25 @@ class AdminSessionInfoTest extends \PHPUnit_Framework_TestCase
     public function testIsLoggedInStatus()
     {
         $this->model->setData('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN);
+        $this->model->setUpdatedAt(901);
+        $this->securityConfigMock->expects($this->once())->method('getAdminSessionLifetime')->willReturn(100);
+        $this->securityConfigMock->expects($this->once())->method('getCurrentTimestamp')->willReturn(1000);
         $this->assertEquals(true, $this->model->isLoggedInStatus());
     }
 
+    /**
+     * @return void
+     */
+    public function testIsLoggedInStatusExpired()
+    {
+        $this->model->setData('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN);
+        $this->model->setUpdatedAt(899);
+        $this->securityConfigMock->expects($this->once())->method('getAdminSessionLifetime')->willReturn(100);
+        $this->securityConfigMock->expects($this->once())->method('getCurrentTimestamp')->willReturn(1000);
+        $this->assertEquals(false, $this->model->isLoggedInStatus());
+        $this->assertEquals(\Magento\Security\Model\AdminSessionInfo::LOGGED_OUT, $this->model->getStatus());
+    }
+
     /**
      * @param bool $expectedResult
      * @param string $sessionLifetime
@@ -96,25 +112,6 @@ class AdminSessionInfoTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
-    /**
-     * @param bool $expectedResult
-     * @param bool $sessionLifetime
-     * @dataProvider dataProviderIsActive
-     */
-    public function testIsActive($expectedResult, $sessionLifetime)
-    {
-        $this->model->setData('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN);
-        $this->securityConfigMock->expects($this->any())
-            ->method('getAdminSessionLifetime')
-            ->will($this->returnValue($sessionLifetime));
-        $this->securityConfigMock->expects($this->any())
-            ->method('getCurrentTimestamp')
-            ->will($this->returnValue(10));
-        $this->model->setUpdatedAt(9);
-
-        $this->assertEquals($expectedResult, $this->model->isActive());
-    }
-
     /**
      * @return array
      */
diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php
index 14ea2b4e2c2ca69d07b826b6b0142713cfddb4a3..23534ff62deddc7f918ac4418e9b747905f2d433 100644
--- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php
@@ -86,7 +86,7 @@ class AuthSessionTest extends \PHPUnit_Framework_TestCase
 
         $this->currentSessionMock = $this->getMock(
             '\Magento\Security\Model\AdminSessionInfo',
-            ['isActive', 'getStatus'],
+            ['isLoggedInStatus', 'getStatus'],
             [],
             '',
             false
@@ -128,7 +128,7 @@ class AuthSessionTest extends \PHPUnit_Framework_TestCase
         };
 
         $this->currentSessionMock->expects($this->once())
-            ->method('isActive')
+            ->method('isLoggedInStatus')
             ->willReturn(false);
 
         $this->authSessionMock->expects($this->once())
@@ -197,7 +197,7 @@ class AuthSessionTest extends \PHPUnit_Framework_TestCase
         };
 
         $this->currentSessionMock->expects($this->any())
-            ->method('isActive')
+            ->method('isLoggedInStatus')
             ->willReturn(true);
 
         $this->adminSessionsManagerMock->expects($this->any())
diff --git a/app/code/Magento/Shipping/Model/Rate/Result.php b/app/code/Magento/Shipping/Model/Rate/Result.php
index 697b91b4d5941629c46b1caf9d4bffdbbecf0fb9..a988e149e9e838aa45efb875594280e062b98836 100644
--- a/app/code/Magento/Shipping/Model/Rate/Result.php
+++ b/app/code/Magento/Shipping/Model/Rate/Result.php
@@ -91,7 +91,7 @@ class Result
     /**
      * Return all quotes in the result
      *
-     * @return array
+     * @return \Magento\Quote\Model\Quote\Address\RateResult\Method[]
      */
     public function getAllRates()
     {
diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
index 69caff420de64d68793217b6fbf210ed6e41ce56..f93d353445b4814c911f5891d70dee5fde410241 100644
--- a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
+++ b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
@@ -24,7 +24,8 @@ use Magento\Swatches\Model\Swatch;
  *
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable
+class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable implements
+    \Magento\Framework\DataObject\IdentityInterface
 {
     /**
      * Path to template file with Swatch renderer.
@@ -106,6 +107,26 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
         );
     }
 
+    /**
+     * Get Key for caching block content
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        return parent::getCacheKey() . '-' . $this->getProduct()->getId();
+    }
+
+    /**
+     * Get block cache life time
+     *
+     * @return int
+     */
+    protected function getCacheLifetime()
+    {
+        return parent::hasCacheLifetime() ? parent::getCacheLifetime() : 3600;
+    }
+
     /**
      * Get Swatch config data
      *
@@ -385,4 +406,18 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
     {
         return $this->getBaseUrl() . self::MEDIA_CALLBACK_ACTION;
     }
+
+    /**
+     * Return unique ID(s) for each object in system
+     *
+     * @return string[]
+     */
+    public function getIdentities()
+    {
+        if ($this->product instanceof \Magento\Framework\DataObject\IdentityInterface) {
+            return $this->product->getIdentities();
+        } else {
+            return [];
+        }
+    }
 }
diff --git a/app/code/Magento/Ui/DataProvider/EavValidationRules.php b/app/code/Magento/Ui/DataProvider/EavValidationRules.php
index c8ae4b77340c4fefc7291c27bf5236ff92909796..18d249dc41da216d7605f306c4c4447b5e4b8242 100644
--- a/app/code/Magento/Ui/DataProvider/EavValidationRules.php
+++ b/app/code/Magento/Ui/DataProvider/EavValidationRules.php
@@ -15,11 +15,9 @@ class EavValidationRules
     /**
      * @var array
      */
-    protected $validationRul = [
-        'input_validation' => [
-            'email' => ['validate-email' => true],
-            'date' => ['validate-date' => true],
-        ],
+    protected $validationRules = [
+        'email' => ['validate-email' => true],
+        'date' => ['validate-date' => true],
     ];
 
     /**
@@ -31,30 +29,23 @@ class EavValidationRules
      */
     public function build(AbstractAttribute $attribute, array $data)
     {
-        $rules = [];
-        if (!empty($data['arguments']['data']['config']['required'])) {
-            $rules['required-entry'] = true;
+        $validation = [];
+        if (isset($data['required']) && $data['required'] == 1) {
+            $validation = array_merge($validation, ['required-entry' => true]);
         }
         if ($attribute->getFrontendInput() === 'price') {
-            $rules['validate-zero-or-greater'] = true;
+            $validation = array_merge($validation, ['validate-zero-or-greater' => true]);
         }
-
-        $validation = $attribute->getValidateRules();
-        if (!empty($validation)) {
-            foreach ($validation as $type => $ruleName) {
-                switch ($type) {
-                    case 'input_validation':
-                        if (isset($this->validationRul[$type][$ruleName])) {
-                            $rules = array_merge($rules, $this->validationRul[$type][$ruleName]);
-                        }
-                        break;
-                    case 'min_text_length':
-                    case 'max_text_length':
-                        $rules = array_merge($rules, [$type => $ruleName]);
-                        break;
-                }
-
+        if ($attribute->getValidateRules()) {
+            $validation = array_merge($validation, $attribute->getValidateRules());
+        }
+        $rules = [];
+        foreach ($validation as $type => $ruleName) {
+            $rule = [$type => $ruleName];
+            if ($type === 'input_validation') {
+                $rule = isset($this->validationRules[$ruleName]) ? $this->validationRules[$ruleName] : [];
             }
+            $rules = array_merge($rules, $rule);
         }
 
         return $rules;
diff --git a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e3248fb9b661685917a1cc55c799b94582bab874
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Ui\Test\Unit\DataProvider;
+
+use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Ui\DataProvider\EavValidationRules;
+
+/**
+ * Class EavValidationRulesTest
+ */
+class EavValidationRulesTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    protected $objectManager;
+
+    /**
+     * @var \Magento\Ui\DataProvider\EavValidationRules
+     */
+    protected $subject;
+
+    /**
+     * @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeMock;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->attributeMock =
+            $this->getMockBuilder(AbstractAttribute::class)
+                ->setMethods(['getFrontendInput', 'getValidateRules'])
+                ->disableOriginalConstructor()
+                ->getMockForAbstractClass();
+
+        $this->subject = new EavValidationRules();
+    }
+
+    /**
+     * @param array $data
+     * @param array $expected
+     * @dataProvider buildDataProvider
+     */
+    public function testBuild($attributeInputType, $validateRules, $data, $expected)
+    {
+        $this->attributeMock->expects($this->once())->method('getFrontendInput')->willReturn($attributeInputType);
+        $this->attributeMock->expects($this->any())->method('getValidateRules')->willReturn($validateRules);
+        $validationRules = $this->subject->build($this->attributeMock, $data);
+        $this->assertEquals($expected, $validationRules);
+    }
+
+    /**
+     * @return array
+     */
+    public function buildDataProvider()
+    {
+        return [
+            ['', '', [], []],
+            ['', null, [], []],
+            ['', false, [], []],
+            ['', [], [], []],
+            ['', '', ['required' => 1], ['required-entry' => true]],
+            ['price', '', [], ['validate-zero-or-greater' => true]],
+            ['price', '', ['required' => 1], ['validate-zero-or-greater' => true, 'required-entry' => true]],
+            ['', ['input_validation' => 'email'], [], ['validate-email' => true]],
+            ['', ['input_validation' => 'date'], [], ['validate-date' => true]],
+            ['', ['input_validation' => 'other'], [], []],
+            ['', ['max_text_length' => '254'], ['required' => 1], ['max_text_length' => 254, 'required-entry' => true]],
+            ['', ['max_text_length' => '254', 'min_text_length' => 1], [],
+                ['max_text_length' => 254, 'min_text_length' => 1]],
+            ['', ['max_text_length' => '254', 'input_validation' => 'date'], [],
+                ['max_text_length' => 254, 'validate-date' => true]],
+        ];
+    }
+}
diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/button.js b/app/code/Magento/Ui/view/base/web/js/form/components/button.js
index 6acce9e43ad6cc5ef976ca6446aaf05717b55018..9fbd07790a30e813a2b30bead14377f619ced490 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/components/button.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/components/button.js
@@ -16,9 +16,9 @@ define([
             additionalClasses: {},
             displayArea: 'outsideGroup',
             displayAsLink: false,
+            visible: true,
             elementTmpl: 'ui/form/element/button',
             template: 'ui/form/components/button/simple',
-            visible: true,
             disabled: false
         },
 
diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
index ec04453079d143e696535f418ddcff3dcdf1e06b..a6cbd245bbaf09bbe586974d8573112cccb421aa 100644
--- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
+++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php
@@ -63,6 +63,7 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         $bind = ['theme_id' => $theme->getId(), 'store_id' => $store->getId()];
         $cacheKey = implode('-', $bind);
         if (!isset($this->layoutUpdateCache[$cacheKey])) {
+            $this->layoutUpdateCache[$cacheKey] = [];
             foreach ($this->getConnection()->fetchAll($this->_getFetchUpdatesByHandleSelect(), $bind) as $layout) {
                 if (!isset($this->layoutUpdateCache[$cacheKey][$layout['handle']])) {
                     $this->layoutUpdateCache[$cacheKey][$layout['handle']] = '';
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html
index 66cf888a6112d27fad29a9e2bb6d653a807e84ce..97c09dd74c91fa6839a78eeb4d0e524a08afdfa2 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html
@@ -13,6 +13,7 @@
 "var payment_html|raw":"Payment Details",
 "var formattedShippingAddress|raw":"Shipping Address",
 "var order.getShippingDescription()":"Shipping Description"
+"var shipping_msg":"Shipping message"
 } @-->
 
 {{template config_path="design/email/header_template"}}
@@ -70,6 +71,9 @@
                     <td class="method-info">
                         <h3>{{trans "Shipping Method"}}</h3>
                         <p>{{var order.getShippingDescription()}}</p>
+                        {{if shipping_msg}}
+                        <p>{{var shipping_msg}}</p>
+                        {{/if}}
                     </td>
                     {{/depend}}
                 </tr>
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html
index d2e42978760d04aac34abf1ce0657ca043d41be6..28c89ba3e264572a156535dd631cb68b677ccbbe 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html
@@ -15,6 +15,7 @@
 "var payment_html|raw":"Payment Details",
 "var formattedShippingAddress|raw":"Shipping Address",
 "var order.getShippingDescription()":"Shipping Description"
+"var shipping_msg":"Shipping message"
 } @-->
 {{template config_path="design/email/header_template"}}
 
@@ -70,6 +71,9 @@
                     <td class="method-info">
                         <h3>{{trans "Shipping Method"}}</h3>
                         <p>{{var order.getShippingDescription()}}</p>
+                        {{if shipping_msg}}
+                        <p>{{var shipping_msg}}</p>
+                        {{/if}}
                     </td>
                     {{/depend}}
                 </tr>
diff --git a/app/etc/di.xml b/app/etc/di.xml
index bc0812e5d423c84983516c9235d1a3c4f0c5885c..ee07e6b9fb21b4a4e17c0627ca4c1af8e35a3df9 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -1075,6 +1075,11 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Validator\Factory">
+        <arguments>
+            <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument>
+        </arguments>
+    </type>
     <type name="Magento\Server\Reflection" shared="false" />
     <type name="Magento\Framework\Reflection\DataObjectProcessor">
         <arguments>
diff --git a/composer.json b/composer.json
index db3b1b781892ec5e2aa952d01748ca36425f4c3a..b5222ae5f4fa824f5a0bde29995e1b5b0458d7fb 100644
--- a/composer.json
+++ b/composer.json
@@ -60,14 +60,14 @@
         "ext-xsl": "*",
         "ext-mbstring": "*",
         "ext-openssl": "*",
-        "ext-zip": "*"
+        "ext-zip": "*",
+        "sjparkinson/static-review": "~4.1"
     },
     "require-dev": {
         "phpunit/phpunit": "4.1.0",
         "squizlabs/php_codesniffer": "1.5.3",
         "phpmd/phpmd": "@stable",
         "pdepend/pdepend": "2.2.2",
-        "sjparkinson/static-review": "~4.1",
         "fabpot/php-cs-fixer": "~1.2",
         "lusitanian/oauth": "~0.3 <=0.7.0"
     },
diff --git a/composer.lock b/composer.lock
index d7525677fdd8f1118add45701b401ebbc9dcc361..bdda4dadaf3f72c4319faa4e7be3f5c5a24f298d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "ac56596efe7d8aa9a070776885b20938",
-    "content-hash": "cedec14d2f9b4094b7aca6bdbbfc17c9",
+    "hash": "a784bbb7f1a3fc568160aa4129b1920f",
+    "content-hash": "d171af939bed91b4acc9b011f456528a",
     "packages": [
         {
             "name": "braintree/braintree_php",
@@ -265,6 +265,55 @@
             ],
             "time": "2016-01-25 15:43:01"
         },
+        {
+            "name": "league/climate",
+            "version": "2.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/climate.git",
+                "reference": "28851c909017424f61cc6a62089316313c645d1c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/climate/zipball/28851c909017424f61cc6a62089316313c645d1c",
+                "reference": "28851c909017424f61cc6a62089316313c645d1c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "dev-master",
+                "phpunit/phpunit": "4.1.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\CLImate\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Joe Tannenbaum",
+                    "email": "hey@joe.codes",
+                    "homepage": "http://joe.codes/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP's best friend for the terminal. CLImate allows you to easily output colored text, special formats, and more.",
+            "keywords": [
+                "cli",
+                "colors",
+                "command",
+                "php",
+                "terminal"
+            ],
+            "time": "2015-01-18 14:31:58"
+        },
         {
             "name": "magento/composer",
             "version": "1.0.2",
@@ -301,16 +350,16 @@
         },
         {
             "name": "magento/magento-composer-installer",
-            "version": "0.1.5",
+            "version": "0.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/magento/magento-composer-installer.git",
-                "reference": "1b33917bfc3f4a0856276dcbe46ce35362f50fc3"
+                "reference": "5b5d29ebe060bc6754c2999206923489db8ca641"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/1b33917bfc3f4a0856276dcbe46ce35362f50fc3",
-                "reference": "1b33917bfc3f4a0856276dcbe46ce35362f50fc3",
+                "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/5b5d29ebe060bc6754c2999206923489db8ca641",
+                "reference": "5b5d29ebe060bc6754c2999206923489db8ca641",
                 "shasum": ""
             },
             "require": {
@@ -373,7 +422,7 @@
                 "composer-installer",
                 "magento"
             ],
-            "time": "2015-09-14 19:59:55"
+            "time": "2016-01-25 22:04:43"
         },
         {
             "name": "magento/zendframework1",
@@ -795,9 +844,62 @@
             ],
             "time": "2015-11-21 02:21:41"
         },
+        {
+            "name": "sjparkinson/static-review",
+            "version": "4.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sjparkinson/static-review.git",
+                "reference": "493c3410cf146a12fca84209bad126c494e125f0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sjparkinson/static-review/zipball/493c3410cf146a12fca84209bad126c494e125f0",
+                "reference": "493c3410cf146a12fca84209bad126c494e125f0",
+                "shasum": ""
+            },
+            "require": {
+                "league/climate": "~2.0",
+                "php": ">=5.4.0",
+                "symfony/console": "~2.0",
+                "symfony/process": "~2.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~0.9",
+                "phpunit/phpunit": "~4.0",
+                "sensiolabs/security-checker": "~2.0",
+                "squizlabs/php_codesniffer": "~1.0"
+            },
+            "suggest": {
+                "sensiolabs/security-checker": "Required for ComposerSecurityReview.",
+                "squizlabs/php_codesniffer": "Required for PhpCodeSnifferReview."
+            },
+            "bin": [
+                "bin/static-review.php"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "StaticReview\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Samuel Parkinson",
+                    "email": "sam.james.parkinson@gmail.com",
+                    "homepage": "http://samp.im"
+                }
+            ],
+            "description": "An extendable framework for version control hooks.",
+            "time": "2014-09-22 08:40:36"
+        },
         {
             "name": "symfony/console",
-            "version": "v2.6.12",
+            "version": "v2.6.13",
             "target-dir": "Symfony/Component/Console",
             "source": {
                 "type": "git",
@@ -855,16 +957,16 @@
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc"
+                "reference": "ee278f7c851533e58ca307f66305ccb9188aceda"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc",
-                "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ee278f7c851533e58ca307f66305ccb9188aceda",
+                "reference": "ee278f7c851533e58ca307f66305ccb9188aceda",
                 "shasum": ""
             },
             "require": {
@@ -911,7 +1013,7 @@
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2015-10-30 20:15:42"
+            "time": "2016-01-13 10:28:07"
         },
         {
             "name": "symfony/finder",
@@ -2710,55 +2812,6 @@
             "description": "A tool to automatically fix PHP code style",
             "time": "2016-01-20 19:00:28"
         },
-        {
-            "name": "league/climate",
-            "version": "2.6.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/thephpleague/climate.git",
-                "reference": "28851c909017424f61cc6a62089316313c645d1c"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/climate/zipball/28851c909017424f61cc6a62089316313c645d1c",
-                "reference": "28851c909017424f61cc6a62089316313c645d1c",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "mockery/mockery": "dev-master",
-                "phpunit/phpunit": "4.1.*"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "League\\CLImate\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Joe Tannenbaum",
-                    "email": "hey@joe.codes",
-                    "homepage": "http://joe.codes/",
-                    "role": "Developer"
-                }
-            ],
-            "description": "PHP's best friend for the terminal. CLImate allows you to easily output colored text, special formats, and more.",
-            "keywords": [
-                "cli",
-                "colors",
-                "command",
-                "php",
-                "terminal"
-            ],
-            "time": "2015-01-18 14:31:58"
-        },
         {
             "name": "lusitanian/oauth",
             "version": "v0.7.0",
@@ -2906,7 +2959,7 @@
                     "name": "Manuel Pichler",
                     "email": "github@manuel-pichler.de",
                     "homepage": "https://github.com/manuelpichler",
-                    "role": "Project Founder"
+                    "role": "Project founder"
                 },
                 {
                     "name": "Other contributors",
@@ -3619,59 +3672,6 @@
             "homepage": "https://github.com/sebastianbergmann/version",
             "time": "2015-06-21 13:59:46"
         },
-        {
-            "name": "sjparkinson/static-review",
-            "version": "4.1.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sjparkinson/static-review.git",
-                "reference": "493c3410cf146a12fca84209bad126c494e125f0"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sjparkinson/static-review/zipball/493c3410cf146a12fca84209bad126c494e125f0",
-                "reference": "493c3410cf146a12fca84209bad126c494e125f0",
-                "shasum": ""
-            },
-            "require": {
-                "league/climate": "~2.0",
-                "php": ">=5.4.0",
-                "symfony/console": "~2.0",
-                "symfony/process": "~2.0"
-            },
-            "require-dev": {
-                "mockery/mockery": "~0.9",
-                "phpunit/phpunit": "~4.0",
-                "sensiolabs/security-checker": "~2.0",
-                "squizlabs/php_codesniffer": "~1.0"
-            },
-            "suggest": {
-                "sensiolabs/security-checker": "Required for ComposerSecurityReview.",
-                "squizlabs/php_codesniffer": "Required for PhpCodeSnifferReview."
-            },
-            "bin": [
-                "bin/static-review.php"
-            ],
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "StaticReview\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Samuel Parkinson",
-                    "email": "sam.james.parkinson@gmail.com",
-                    "homepage": "http://samp.im"
-                }
-            ],
-            "description": "An extendable framework for version control hooks.",
-            "time": "2014-09-22 08:40:36"
-        },
         {
             "name": "squizlabs/php_codesniffer",
             "version": "1.5.3",
@@ -3910,16 +3910,16 @@
         },
         {
             "name": "symfony/stopwatch",
-            "version": "v3.0.1",
+            "version": "v3.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/stopwatch.git",
-                "reference": "6aeac8907e3e1340a0033b0a9ec075f8e6524800"
+                "reference": "4a204804952ff267ace88cf499e0b4bb302a475e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/stopwatch/zipball/6aeac8907e3e1340a0033b0a9ec075f8e6524800",
-                "reference": "6aeac8907e3e1340a0033b0a9ec075f8e6524800",
+                "url": "https://api.github.com/repos/symfony/stopwatch/zipball/4a204804952ff267ace88cf499e0b4bb302a475e",
+                "reference": "4a204804952ff267ace88cf499e0b4bb302a475e",
                 "shasum": ""
             },
             "require": {
@@ -3955,7 +3955,7 @@
             ],
             "description": "Symfony Stopwatch Component",
             "homepage": "https://symfony.com",
-            "time": "2015-10-30 23:35:59"
+            "time": "2016-01-03 15:35:16"
         },
         {
             "name": "symfony/yaml",
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeOptionManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeOptionManagementInterfaceTest.php
index 2e31bec6182ee8d142cab94e7bc18eb3e49d9354..6f55dff0ba9f7d4b83a6799e16b7f44d8775b7f8 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeOptionManagementInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeOptionManagementInterfaceTest.php
@@ -69,7 +69,6 @@ class ProductAttributeOptionManagementInterfaceTest extends WebapiAbstract
 
         $optionData = [
             AttributeOptionInterface::LABEL => 'new color',
-            AttributeOptionInterface::VALUE => 'grey',
             AttributeOptionInterface::SORT_ORDER => 100,
             AttributeOptionInterface::IS_DEFAULT => true,
             AttributeOptionInterface::STORE_LABELS => [
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php
index 41032701784ce7c9788a9da60abb28a2516ce807..7b8e51fb2a7a4d93e46a16746b8b29f0bc4c2149 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php
@@ -84,56 +84,26 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web
 
         $expectedData = [
             'attribute_code' => $attributeCode,
-            'frontend_labels' => [
-                [
-                    'store_id' => 0,
-                    'label' => 'front_lbl'
-                ],
-            ],
             'is_required' => true,
-            "default_value" => "",
             "frontend_input" => "select",
             "is_visible_on_front" => true,
             "is_searchable" => true,
             "is_visible_in_advanced_search" => true,
             "is_filterable" => true,
             "is_filterable_in_search" => true,
-            "options" => [
-                [
-                    "label" => "string",
-                    "value" => "string",
-                    "sort_order" => 0,
-                    "is_default" => true,
-                    "store_labels" => [
-                        [
-                            "store_id" => 0,
-                            "label" => "Red"
-                        ],
-                        [
-                            "store_id" => 1,
-                            "label" => "Blue"
-                        ],
-                        [
-                            "store_id" => 2,
-                            "label" => "Yellow"
-                        ]
-                    ]
-                ]
-            ]
         ];
 
-        $this->assertEquals($attribute['options'], $expectedData['options']);
-        $this->assertEquals($attribute['attribute_code'], $expectedData['attribute_code']);
-        $this->assertEquals($attribute['frontend_labels'], $expectedData['frontend_labels']);
-        $this->assertEquals($attribute['is_required'], $expectedData['is_required']);
-        $this->assertEquals($attribute['default_value'], $expectedData['default_value']);
-        $this->assertEquals($attribute['frontend_input'], $expectedData['frontend_input']);
-        $this->assertEquals($attribute['is_filterable'], $expectedData['is_filterable']);
-        $this->assertEquals($attribute['is_filterable_in_search'], $expectedData['is_filterable_in_search']);
-        $this->assertEquals(
-            $attribute['is_visible_in_advanced_search'],
-            $expectedData['is_visible_in_advanced_search']
-        );
+        $this->assertEquals('front_lbl', $attribute['default_frontend_label']);
+        foreach ($expectedData as $key => $value) {
+            $this->assertEquals($value, $attribute[$key]);
+        }
+        //Validate options
+        //'Blue' should be first as it has sort_order = 0
+        $this->assertEquals('Default Blue', $attribute['options'][1]['label']);
+        $this->assertArrayHasKey('default_value', $attribute);
+        //'Blue' should be set as default
+        $this->assertEquals($attribute['default_value'], $attribute['options'][1]['value']);
+        $this->assertEquals('Default Red', $attribute['options'][2]['label']);
     }
 
     /**
@@ -153,46 +123,111 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web
     }
 
     /**
-     * @magentoApiDataFixture Magento/Catalog/_files/product_attribute.php
+     * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php
      */
     public function testUpdate()
     {
-        $attributeCode = 'test_attribute_code_333';
-        $attribute = $this->getAttribute($attributeCode);
+        $attributeCode = 'label_attr_code3df4tr3';
+        $attribute = $this->createAttribute($attributeCode);
 
+        //Make sure that 'Blue' is set as default
+        $this->assertEquals($attribute['default_value'], $attribute['options'][1]['value']);
         $attributeData = [
             'attribute' => [
                 'attribute_id' => $attribute['attribute_id'],
                 'frontend_labels' => [
                     ['store_id' => 0, 'label' => 'front_lbl_new'],
                 ],
-                'default_value' => 'default value new',
+                "options" => [
+                    //Update existing
+                    [
+                        "value" => $attribute['options'][1]['value'],
+                        "label" => "New Label",
+                        "store_labels" => [
+                            [
+                                "store_id" => 1,
+                                "label" => "Default Blue Updated"
+                            ]
+                        ]
+                    ],
+                    //Add new option
+                    [
+                        "label" => "Green",
+                        "value" => "",
+                        "sort_order" => 200,
+                        "is_default" => true,
+                        "store_labels" => [
+                            [
+                                "store_id" => 0,
+                                "label" => "Admin Green"
+                            ],
+                            [
+                                "store_id" => 1,
+                                "label" => "Default Green"
+                            ]
+                        ]
+                    ]
+                ],
                 'is_required' => false,
-                'frontend_input' => 'text',
-            ],
-        ];
-
-        $serviceInfo = [
-            'rest' => [
-                'resourcePath' => self::RESOURCE_PATH . '/' . $attributeCode,
-                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
-            ],
-            'soap' => [
-                'service' => self::SERVICE_NAME,
-                'serviceVersion' => self::SERVICE_VERSION,
-                'operation' => self::SERVICE_NAME . 'Save',
+                'frontend_input' => 'select',
             ],
         ];
-
-        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
-            $attributeData['attribute']['attributeCode'] = $attributeCode;
-        }
-        $result = $this->_webApiCall($serviceInfo, $attributeData);
+        $result = $this->updateAttribute($attributeCode, $attributeData);
 
         $this->assertEquals($attribute['attribute_id'], $result['attribute_id']);
         $this->assertEquals($attributeCode, $result['attribute_code']);
-        $this->assertEquals('default value new', $result['default_value']);
         $this->assertEquals('front_lbl_new', $result['default_frontend_label']);
+        //New option set as default
+        $this->assertEquals($result['options'][3]['value'], $result['default_value']);
+        $this->assertEquals("Default Blue Updated", $result['options'][1]['label']);
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php
+     */
+    public function testUpdateNotExistingOption()
+    {
+        $attributeCode = 'label_attr_code3df4tr3';
+        $attribute = $this->createAttribute($attributeCode);
+        $expectedMessage = 'Attribute %1 does not contain option with Id %2';
+
+        $attributeData = [
+            'attribute' => [
+                'attribute_id' => $attribute['attribute_id'],
+                'is_required' => true,
+                'frontend_labels' => [
+                    ['store_id' => 0, 'label' => 'front_lbl_new'],
+                ],
+                "options" => [
+                    [
+                        "value" => 1,
+                        "label" => "New Label",
+                        "store_labels" => [
+                            [
+                                "store_id" => 1,
+                                "label" => "new label"
+                            ]
+                        ]
+                    ],
+                ],
+                'frontend_input' => 'select',
+            ]
+        ];
+
+        try {
+            $this->updateAttribute($attributeCode, $attributeData);
+            $this->fail("Expected exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expectedMessage,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $errorObj = $this->processRestExceptionResult($e);
+            $this->assertEquals($expectedMessage, $errorObj['message']);
+            $this->assertEquals([$attributeCode, 1], $errorObj['parameters']);
+        }
     }
 
     /**
@@ -204,9 +239,6 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web
         $this->assertTrue($this->deleteAttribute($attributeCode));
     }
 
-    /**
-     * @magentoApiDataFixture Magento/Catalog/_files/product_attribute.php
-     */
     public function testDeleteNoSuchEntityException()
     {
         $attributeCode = 'some_test_code';
@@ -266,22 +298,34 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web
                 "is_filterable_in_search" => true,
                 "options" => [
                     [
-                        "label" => "string",
-                        "value" => "string",
-                        "sort_order" => 0,
-                        "is_default" => true,
+                        "label" => "Red",
+                        "value" => "",
+                        "sort_order" => 100,
+                        "is_default" => false,
                         "store_labels" => [
                             [
                                 "store_id" => 0,
-                                "label" => "Red"
+                                "label" => "Admin Red"
                             ],
                             [
                                 "store_id" => 1,
-                                "label" => "Blue"
+                                "label" => "Default Red"
+                            ]
+                        ]
+                    ],
+                    [
+                        "label" => "Blue",
+                        "value" => "",
+                        "sort_order" => 0,
+                        "is_default" => true,
+                        "store_labels" => [
+                            [
+                                "store_id" => 0,
+                                "label" => "Admin Blue"
                             ],
                             [
-                                "store_id" => 2,
-                                "label" => "Yellow"
+                                "store_id" => 1,
+                                "label" => "Default Blue"
                             ]
                         ]
                     ]
@@ -345,4 +389,29 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web
         ];
         return $this->_webApiCall($serviceInfo, ['attributeCode' => $attributeCode]);
     }
+
+    /**
+     * Update attribute by code
+     *
+     * @param $attributeCode
+     * @return array|bool|float|int|string
+     */
+    protected function updateAttribute($attributeCode, $attributeData)
+    {
+        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
+            $attributeData['attribute']['attributeCode'] = $attributeCode;
+        }
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $attributeCode,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        return $this->_webApiCall($serviceInfo, $attributeData);
+    }
 }
diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/DropdownmultiselectElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/DropdownmultiselectElement.php
index f698d6014e87b70169158ada5398a9b772c07176..302dc54c8fa68bac68af84dd60af56e90a5a44c5 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/DropdownmultiselectElement.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/DropdownmultiselectElement.php
@@ -32,7 +32,7 @@ class DropdownmultiselectElement extends MultiselectElement
      *
      * @var string
      */
-    protected $optionByValue = './/li[label[contains(normalize-space(.), %s)]]';
+    protected $optionByValue = './/li//label[contains(normalize-space(.), %s)]';
 
     /**
      * Set values.
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireAdminSessionTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireAdminSessionTest.php
index 5e29ed43311ce3d3538c6e12c4532a126564d0bb..dcadc4165c7552d72277a9652350af9fd5e37941 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireAdminSessionTest.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireAdminSessionTest.php
@@ -7,6 +7,7 @@
 namespace Magento\Backend\Test\TestCase;
 
 use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
+use Magento\Backend\Test\Page\AdminAuthLogin;
 use Magento\Config\Test\Fixture\ConfigData;
 use Magento\Mtf\TestCase\Injectable;
 
@@ -46,10 +47,14 @@ class ExpireAdminSessionTest extends Injectable
      *
      * @param SystemConfigEdit $systemConfigEdit
      * @param ConfigData $sessionLifetimeConfig
+     * @param AdminAuthLogin $adminAuthLogin
      * @return void
      */
-    public function test(SystemConfigEdit $systemConfigEdit, ConfigData $sessionLifetimeConfig)
-    {
+    public function test(
+        SystemConfigEdit $systemConfigEdit,
+        ConfigData $sessionLifetimeConfig,
+        AdminAuthLogin $adminAuthLogin
+    ) {
         $this->systemConfigEdit = $systemConfigEdit;
         $this->sessionLifetimeConfig = $sessionLifetimeConfig;
         $this->systemConfigEdit->open();
@@ -71,7 +76,7 @@ class ExpireAdminSessionTest extends Injectable
          */
         sleep($section[$keys[0]]['label']);
 
-        $this->systemConfigEdit->open();
+        $adminAuthLogin->open();
     }
 
     /**
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/NavigateMenuTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/NavigateMenuTest.xml
index 847a1f2c242e6cbc840de37609d98898dd308a48..02535cf1bf01d5fc35f8c3bedcaaf64e634d64e3 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/NavigateMenuTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/NavigateMenuTest.xml
@@ -14,7 +14,7 @@
         </variation>
         <variation name="NavigateMenuTest10">
             <data name="menuItem" xsi:type="string">Products > Categories</data>
-            <data name="pageTitle" xsi:type="string">Categories</data>
+            <data name="pageTitle" xsi:type="string">Default Category</data>
             <constraint name="Magento\Backend\Test\Constraint\AssertBackendPageIsAvailable"/>
         </variation>
         <variation name="NavigateMenuTest11">
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml
index 51cd1e54461e37fe1a48002f66635beec9a0d103..ae909b6ef26245cfa8487459ec318fb12d223623 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml
@@ -12,7 +12,8 @@
             <input>multiselectgrouplist</input>
         </stores>
         <is_active>
-            <input>checkbox</input>
+            <selector>.admin__actions-switch-label</selector>
+            <input>switcher</input>
         </is_active>
     </fields>
 </mapping>
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.xml
index 61805c1c1ed3bdfd9aee91114c3ef411be527f23..c6bea89c36dca31d7962ea4ae612d600c25d391d 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.xml
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.xml
@@ -13,7 +13,8 @@
         <fields>
             <title />
             <is_active>
-                <input>checkbox</input>
+                <selector>.admin__actions-switch-label</selector>
+                <input>switcher</input>
             </is_active>
         </fields>
     </page_information>
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php
index 9402c76580fb4ecf768668ca62e6ab3470aea0a8..4293a6a7faf0509964ef0720b3e023ae53290070 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php
@@ -59,6 +59,13 @@ class Content extends Tab
      */
     protected $contentHeading = '[name="content_heading"]';
 
+    /**
+     * Header locator.
+     *
+     * @var string
+     */
+    protected $header = 'header.page-header';
+
     /**
      * Clicking in content tab 'Insert Variable' button.
      *
@@ -85,7 +92,12 @@ class Content extends Tab
         $context = $element === null ? $this->_rootElement : $element;
         $addWidgetButton = $context->find($this->addWidgetButton);
         if ($addWidgetButton->isVisible()) {
-            $addWidgetButton->click();
+            try {
+                $addWidgetButton->click();
+            } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
+                $this->browser->find($this->header)->hover();
+                $addWidgetButton->click();
+            }
         }
     }
 
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotInGrid.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotInGrid.php
index ad3ce5daef521523e6e439da296394955d995c81..2742eaee67c0f101baba23c35ac5ce17e0217ad8 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotInGrid.php
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotInGrid.php
@@ -29,6 +29,7 @@ class AssertCmsBlockNotInGrid extends AbstractConstraint
     {
         $cmsBlockIndex->open();
         $data = $cmsBlock->getData();
+        $data['is_active'] = $data['is_active'] == 'Yes' ?  'Enabled' : 'Disabled';
         if (isset($data['stores'])) {
             $storeId = is_array($data['stores']) ? reset($data['stores']) : $data['stores'];
             $parts = explode("/", $storeId);
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php
index d5de5868dba76339fe1fc00741d348f9bf65c3d1..80c66c31d9db3b448abd34d82b5abcda39333343 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php
@@ -30,6 +30,20 @@ class Register extends Form
      */
     protected $customerAttribute = "[name='%s']";
 
+    /**
+     * Locator for password error
+     *
+     * @var string
+     */
+    protected $passwordError = "#password-error";
+
+    /**
+     * Locator for password confirmation error
+     *
+     * @var string
+     */
+    protected $passwordConfirmationError = "#password-confirmation-error";
+
     /**
      * Create new customer account and fill billing address if it exists
      *
@@ -44,4 +58,26 @@ class Register extends Form
         }
         $this->_rootElement->find($this->submit, Locator::SELECTOR_CSS)->click();
     }
+
+    /**
+     * Get password error on new customer registration form.
+     *
+     * @return string
+     *
+     */
+    public function getPasswordError()
+    {
+        return $this->_rootElement->find($this->passwordError, Locator::SELECTOR_CSS)->getText();
+    }
+
+    /**
+     * Get password confirmation error on new customer registration form.
+     *
+     * @return string
+     *
+     */
+    public function getPasswordConfirmationError()
+    {
+        return $this->_rootElement->find($this->passwordConfirmationError, Locator::SELECTOR_CSS)->getText();
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml
index 08af0a568a63a7eefed7903ab59ead405e7d65dc..a57ce7ec5a2343a09efb44066ee6f9e0b8242bc7 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml
@@ -11,8 +11,8 @@
             <data name="customer/data/firstname" xsi:type="string">john</data>
             <data name="customer/data/lastname" xsi:type="string">doe</data>
             <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data>
-            <data name="customer/data/password" xsi:type="string">123123q</data>
-            <data name="customer/data/password_confirmation" xsi:type="string">123123q</data>
+            <data name="customer/data/password" xsi:type="string">123123q#</data>
+            <data name="customer/data/password_confirmation" xsi:type="string">123123q#</data>
             <constraint name="Magento\Customer\Test\Constraint\AssertCustomerFailRegisterMessage" />
         </variation>
     </testCase>
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml
index 061ebbfd7518ad9dbab4055045ee86d56f6aa17d..a61daa8fd10e628805828390b6eb6222071fdba4 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml
@@ -41,23 +41,5 @@
             <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessRegisterMessage" />
             <constraint name="Magento\Customer\Test\Constraint\AssertCustomerRedirectToDashboard" />
         </variation>
-        <variation name="RegisterCustomerFrontendEntityTestVariation4" summary="Customer password length is below 8 characters">
-            <data name="customer/data/firstname" xsi:type="string">john</data>
-            <data name="customer/data/lastname" xsi:type="string">doe</data>
-            <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data>
-            <data name="customer/data/is_subscribed" xsi:type="string">No</data>
-            <data name="customer/data/password" xsi:type="string">123123q</data>
-            <data name="customer/data/password_confirmation" xsi:type="string">123123q</data>
-            <constraint name="Magento\Customer\Test\Constraint\AssertPasswordLengthErrorMessage" />
-        </variation>
-        <variation name="RegisterCustomerFrontendEntityTestVariation5" summary="Customer password is not secure enough">
-            <data name="customer/data/firstname" xsi:type="string">john</data>
-            <data name="customer/data/lastname" xsi:type="string">doe</data>
-            <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data>
-            <data name="customer/data/is_subscribed" xsi:type="string">No</data>
-            <data name="customer/data/password" xsi:type="string">123123qw</data>
-            <data name="customer/data/password_confirmation" xsi:type="string">123123qw</data>
-            <constraint name="Magento\Customer\Test\Constraint\AssertPasswordIsNotSecureEnoughMessage" />
-        </variation>
     </testCase>
 </config>
diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/LockAdminUserWhenCreatingNewIntegrationTest.php b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/LockAdminUserWhenCreatingNewIntegrationTest.php
index 5d3336f9068174c01f2da08ab2210088ff77b27a..52b071596e145faf52497b8443e8a5942ed2042f 100644
--- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/LockAdminUserWhenCreatingNewIntegrationTest.php
+++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/LockAdminUserWhenCreatingNewIntegrationTest.php
@@ -107,6 +107,7 @@ class LockAdminUserWhenCreatingNewIntegrationTest extends Injectable
         $this->adminAuthLogin->open();
         $this->adminAuthLogin->getLoginBlock()->fill($customAdmin);
         $this->adminAuthLogin->getLoginBlock()->submit();
+
         // Steps
         $this->integrationIndexPage->open();
         $this->integrationIndexPage->getGridPageActions()->addNew();
@@ -114,6 +115,11 @@ class LockAdminUserWhenCreatingNewIntegrationTest extends Injectable
             $this->integrationNewPage->getIntegrationForm()->fill($integration);
             $this->integrationNewPage->getFormPageActions()->saveNew();
         }
+
+        // Reload page
+        $this->adminAuthLogin->open();
+        $this->adminAuthLogin->getLoginBlock()->fill($customAdmin);
+        $this->adminAuthLogin->getLoginBlock()->submit();
     }
 
     /**
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.php
index 13b674ade8077da81bc1dff92597c55abd9618ab..eefedf3069bdcc3d9bb0b81ddb00bceaa170140c 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.php
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.php
@@ -45,7 +45,6 @@ class CreateOrderBackendTest extends Scenario
      */
     public function test()
     {
-        $this->markTestIncomplete('MAGETWO-48742');
         $this->executeScenario();
     }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php
similarity index 83%
rename from dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php
rename to dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php
index 7eff752f870a5cc1e99e8bf2089e65c8915f57b5..ce0eaf98e091b84c586d44c3e6c298a9f8109ce3 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordIsNotSecureEnoughMessage.php
@@ -4,7 +4,7 @@
  * See COPYING.txt for license details.
  */
 
-namespace Magento\Customer\Test\Constraint;
+namespace Magento\Security\Test\Constraint;
 
 use Magento\Customer\Test\Page\CustomerAccountCreate;
 use Magento\Mtf\Constraint\AbstractConstraint;
@@ -23,9 +23,9 @@ class AssertPasswordIsNotSecureEnoughMessage extends AbstractConstraint
      */
     public function processAssert(CustomerAccountCreate $registerPage)
     {
-        $expectedErrorMessage = 'Minimum different classes of characters in password are 3.' .
+        $expectedErrorMessage = 'Minimum of different classes of characters in password is 3.' .
             ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.';
-        $errorMessage = $registerPage->getMessagesBlock()->getErrorMessage();
+        $errorMessage = $registerPage->getRegisterForm()->getPasswordError();
         \PHPUnit_Framework_Assert::assertEquals(
             $expectedErrorMessage,
             $errorMessage,
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordLengthErrorMessage.php b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordLengthErrorMessage.php
similarity index 74%
rename from dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordLengthErrorMessage.php
rename to dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordLengthErrorMessage.php
index 3a985a74b0281abc0243978f105129fc55535f6a..12a3bf248913ae3f0f973e528b98ed3287ebe57f 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertPasswordLengthErrorMessage.php
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertPasswordLengthErrorMessage.php
@@ -4,7 +4,7 @@
  * See COPYING.txt for license details.
  */
 
-namespace Magento\Customer\Test\Constraint;
+namespace Magento\Security\Test\Constraint;
 
 use Magento\Customer\Test\Page\CustomerAccountCreate;
 use Magento\Mtf\Constraint\AbstractConstraint;
@@ -14,7 +14,7 @@ use Magento\Mtf\Constraint\AbstractConstraint;
  */
 class AssertPasswordLengthErrorMessage extends AbstractConstraint
 {
-    const PASSWORD_LENGTH_ERROR_MESSAGE = 'Please enter a password with at least 8 characters.';
+    const PASSWORD_LENGTH_ERROR_MESSAGE = 'Minimum length of this field must be equal or greater than 8 symbols';
 
     /**
      * Assert that appropriate message is displayed on "Create New Customer Account" page(frontend) if password length
@@ -25,11 +25,11 @@ class AssertPasswordLengthErrorMessage extends AbstractConstraint
      */
     public function processAssert(CustomerAccountCreate $registerPage)
     {
-        $errorMessage = $registerPage->getMessagesBlock()->getErrorMessage();
-        \PHPUnit_Framework_Assert::assertEquals(
+        $errorMessage = $registerPage->getRegisterForm()->getPasswordError();
+        \PHPUnit_Framework_Assert::assertContains(
             self::PASSWORD_LENGTH_ERROR_MESSAGE,
             $errorMessage,
-            'The messages are not equal.'
+            'Incorrect password error message.'
         );
     }
 
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.php b/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.php
new file mode 100644
index 0000000000000000000000000000000000000000..20e35317df6d788788c950d67e4110d3e34e08f0
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Security\Test\Page;
+
+use Magento\Mtf\Page\Page;
+
+/**
+ * Class UserAccountForgotPassword
+ */
+class UserAccountForgotPassword extends Page
+{
+    const MCA = 'admin/auth/forgotpassword';
+
+    /**
+     * Blocks' config
+     *
+     * @var array
+     */
+    protected $blocks = [
+        'messagesBlock' => [
+            'class' => 'Magento\Backend\Test\Block\Messages',
+            'locator' => '.messages',
+            'strategy' => 'css selector',
+        ],
+        'forgotPasswordForm' => [
+            'class' => 'Magento\Security\Test\Block\Form\ForgotPassword',
+            'locator' => '#login-form',
+            'strategy' => 'css selector',
+        ],
+    ];
+
+    /**
+     * Constructor.
+     */
+    protected function initUrl()
+    {
+        $this->url = $_ENV['app_backend_url'] . self::MCA;
+    }
+
+    /**
+     * @return \Magento\Backend\Test\Block\Messages
+     */
+    public function getMessagesBlock()
+    {
+        return $this->getBlockInstance('messagesBlock');
+    }
+
+    /**
+     * @return \Magento\Security\Test\Block\Form\ForgotPassword
+     */
+    public function getForgotPasswordForm()
+    {
+        return $this->getBlockInstance('forgotPasswordForm');
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.xml b/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.xml
deleted file mode 100644
index 7d467309126831fafbe47ebdd186c5ea58879be9..0000000000000000000000000000000000000000
--- a/dev/tests/functional/tests/app/Magento/Security/Test/Page/UserAccountForgotPassword.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
- -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd">
-    <page name="UserAccountForgotPassword" mca="admin/admin/auth/forgotpassword" module="Magento_Security">
-        <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator=".messages" strategy="css selector"/>
-        <block name="forgotPasswordForm" class="Magento\Security\Test\Block\Form\ForgotPassword" locator="#login-form" strategy="css selector"/>
-    </page>
-</config>
\ No newline at end of file
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..77a37dc4369e51a296a786f13ed3e32fb9763f65
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Security\Test\TestCase;
+
+use Magento\Customer\Test\Fixture\Customer;
+use Magento\Customer\Test\Page\CustomerAccountCreate;
+use Magento\Cms\Test\Page\CmsIndex;
+use Magento\Mtf\TestCase\Injectable;
+
+/**
+ * Test Flow:
+ * 1. Go to frontend.
+ * 2. Click Register link.
+ * 3. Fill registry form.
+ * 4. Click 'Create account' button.
+ * 5. Perform assertions.
+ *
+ * @ZephyrId MAGETWO-49044
+ */
+class NewFrontendCustomerPasswordTest extends Injectable
+{
+    /* tags */
+    const MVP = 'yes';
+    const DOMAIN = 'PS';
+    /* end tags */
+
+    /**
+     * Customer registry page.
+     *
+     * @var CustomerAccountCreate
+     */
+    protected $customerAccountCreate;
+
+    /**
+     * Cms page.
+     *
+     * @var CmsIndex $cmsIndex
+     */
+    protected $cmsIndex;
+
+    /**
+     * Inject data.
+     *
+     * @param CustomerAccountCreate $customerAccountCreate
+     * @param CmsIndex $cmsIndex
+     * @return void
+     */
+    public function __inject(
+        CustomerAccountCreate $customerAccountCreate,
+        CmsIndex $cmsIndex
+    ) {
+        $this->customerAccountCreate = $customerAccountCreate;
+        $this->cmsIndex = $cmsIndex;
+    }
+
+    /**
+     * Create Customer account on Storefront.
+     *
+     * @param Customer $customer
+     * @return void
+     */
+    public function test(Customer $customer)
+    {
+        // Steps
+        $this->cmsIndex->open();
+        $this->cmsIndex->getLinksBlock()->openLink('Create an Account');
+        $this->customerAccountCreate->getRegisterForm()->registerCustomer($customer);
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5426cc7679bc8131f1dee2c6e2ab702d02b6deb6
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewFrontendCustomerPasswordTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+ -->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd">
+    <testCase name="Magento\Security\Test\TestCase\NewFrontendCustomerPasswordTest" summary="New frontend customer password check" ticketId="MAGETWO-49044">
+        <variation name="PasswordLengthTest" summary="Customer password length is below 8 characters">
+            <data name="customer/data/firstname" xsi:type="string">john</data>
+            <data name="customer/data/lastname" xsi:type="string">doe</data>
+            <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data>
+            <data name="customer/data/is_subscribed" xsi:type="string">No</data>
+            <data name="customer/data/password" xsi:type="string">123123</data>
+            <data name="customer/data/password_confirmation" xsi:type="string">123123</data>
+            <constraint name="Magento\Security\Test\Constraint\AssertPasswordLengthErrorMessage" />
+        </variation>
+        <variation name="PasswordComplexityTest" summary="Customer password is not secure enough">
+            <data name="customer/data/firstname" xsi:type="string">john</data>
+            <data name="customer/data/lastname" xsi:type="string">doe</data>
+            <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data>
+            <data name="customer/data/is_subscribed" xsi:type="string">No</data>
+            <data name="customer/data/password" xsi:type="string">123123qa</data>
+            <data name="customer/data/password_confirmation" xsi:type="string">123123qa</data>
+            <constraint name="Magento\Security\Test\Constraint\AssertPasswordIsNotSecureEnoughMessage" />
+        </variation>
+    </testCase>
+</config>
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserIsLocked.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserIsLocked.php
index c2aef263fce46fbba4b88437e29111bb1d47dbb5..1a3631c74e5379bfe6afe18aa46c161d4ae1e774 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserIsLocked.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserIsLocked.php
@@ -14,7 +14,7 @@ use Magento\Mtf\Constraint\AbstractConstraint;
  */
 class AssertUserIsLocked extends AbstractConstraint
 {
-    const USER_ACCOUNT_DISABLED_MESSAGE = 'Your account is temporarily disabled.';
+    const USER_ACCOUNT_DISABLED_MESSAGE = 'account is temporarily disabled';
 
     /**
      * Verify that user account has been locked.
@@ -25,10 +25,12 @@ class AssertUserIsLocked extends AbstractConstraint
     public function processAssert(
         AdminAuthLogin $adminAuth
     ) {
-        \PHPUnit_Framework_Assert::assertEquals(
+        $ignoreCase = true;
+        \PHPUnit_Framework_Assert::assertContains(
             self::USER_ACCOUNT_DISABLED_MESSAGE,
             $adminAuth->getMessagesBlock()->getErrorMessage(),
-            'Message "' . self::USER_ACCOUNT_DISABLED_MESSAGE . '" is not visible.'
+            'Message "' . self::USER_ACCOUNT_DISABLED_MESSAGE . '" is not visible.',
+            $ignoreCase
         );
     }
 
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.php
index 41d0ec81d7a950d05c6055493ea35378b7e50c7a..87e8a9c971e87dac46f57df66003f4e7d1d99be4 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.php
@@ -103,7 +103,7 @@ class LockAdminUserWhenCreatingNewRoleTest extends Injectable
         )->run();
         $customAdmin->persist();
 
-        //Steps
+        // Steps
         $this->adminAuthLogin->open();
         $this->adminAuthLogin->getLoginBlock()->fill($customAdmin);
         $this->adminAuthLogin->getLoginBlock()->submit();
@@ -114,6 +114,11 @@ class LockAdminUserWhenCreatingNewRoleTest extends Injectable
             $this->userRoleEditRole->getRoleFormTabs()->fill($role);
             $this->userRoleEditRole->getPageActions()->save();
         }
+
+        // Reload
+        $this->adminAuthLogin->open();
+        $this->adminAuthLogin->getLoginBlock()->fill($customAdmin);
+        $this->adminAuthLogin->getLoginBlock()->submit();
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d47c0b49915b93045298d03059a8f9ce22e7e3c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Model\Category;
+
+use Magento\Catalog\Model\Category\DataProvider;
+use Magento\Eav\Model\Config as EavConfig;
+use Magento\TestFramework\Helper\Bootstrap;
+
+class DataProviderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var DataProvider
+     */
+    private $dataProvider;
+
+    /**
+     * @var \Magento\Eav\Model\Entity\Type
+     */
+    private $entityType;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $objectManager = Bootstrap::getObjectManager();
+        $this->dataProvider = $objectManager->create(
+            DataProvider::class,
+            [
+                'name' => 'category_form_data_source',
+                'primaryFieldName' => 'entity_id',
+                'requestFieldName' => 'id'
+            ]
+        );
+
+        $this->entityType = $objectManager->create(EavConfig::class)->getEntityType('catalog_category');
+    }
+
+    /**
+     * @return void
+     */
+    public function testGetMetaRequiredAttributes()
+    {
+        $requiredAttributes = [
+            'general' => ['name'],
+            'display_settings' => ['available_sort_by', 'default_sort_by'],
+        ];
+        $meta = $this->dataProvider->getMeta();
+        $this->assertArrayHasKey('url_key', $meta['search_engine_optimization']['children']);
+        foreach ($requiredAttributes as $scope => $attributes) {
+            foreach ($attributes as $attribute) {
+                $this->assertArrayHasKey($attribute, $meta[$scope]['children']);
+                $data = $meta[$scope]['children'][$attribute];
+                $this->assertTrue($data['arguments']['data']['config']['validation']['required-entry']);
+            }
+        }
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTreeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTreeTest.php
index 46a36dbf0a0230214c77a0daadb096387390a985..3540b9ad076ac280d830352f9a1077bf2088512d 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTreeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTreeTest.php
@@ -125,7 +125,7 @@ class CategoryTreeTest extends \PHPUnit_Framework_TestCase
     public function testGetChildren()
     {
         $this->_model->load(3);
-        $this->assertEquals('4', $this->_model->getChildren());
+        $this->assertEquals('4,13', $this->_model->getChildren());
     }
 
     public function testGetPathInStore()
@@ -186,7 +186,7 @@ class CategoryTreeTest extends \PHPUnit_Framework_TestCase
     {
         $this->_model->load(3);
         $children = $this->_model->getChildrenCategories();
-        $this->assertEquals(1, count($children));
+        $this->assertEquals(2, count($children));
     }
 
     public function testGetChildrenCategoriesEmpty()
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/CategoryTest.php
index 2a2f4c6c1da25765fc81ed1a93e511589b575da9..26729a485c31e0b4ca9c0330da00248595af6850 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/CategoryTest.php
@@ -126,11 +126,10 @@ class CategoryTest extends \PHPUnit_Framework_TestCase
         $this->assertInstanceOf('Magento\Catalog\Model\Category', $category);
         $this->assertEquals(3, $category->getId());
 
-
         $items = $model->getItems();
 
         $this->assertInternalType('array', $items);
-        $this->assertEquals(1, count($items));
+        $this->assertEquals(2, count($items));
 
         /** @var $item \Magento\Catalog\Model\Layer\Filter\Item */
         $item = $items[0];
@@ -139,6 +138,12 @@ class CategoryTest extends \PHPUnit_Framework_TestCase
         $this->assertSame($model, $item->getFilter());
         $this->assertEquals('Category 1.1', $item->getLabel());
         $this->assertEquals(4, $item->getValue());
-        $this->assertEquals(1, $item->getCount());
+        $this->assertEquals(2, $item->getCount());
+
+        $item = $items[1];
+        $this->assertInstanceOf('Magento\Catalog\Model\Layer\Filter\Item', $item);
+        $this->assertEquals('Category 1.2', $item->getLabel());
+        $this->assertEquals(13, $item->getValue());
+        $this->assertEquals(2, $item->getCount());
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php
index c87b553e18f8b4772ef7a5d562161cafa2508d21..3b39cfe48a314e0797b6938742b7354d54329d57 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php
@@ -112,7 +112,7 @@ class ProductExternalTest extends \PHPUnit_Framework_TestCase
         $this->_model->setId(
             $this->productRepository->get('simple')->getId()
         );
-        $this->assertEquals([2, 3, 4], $this->_model->getCategoryIds());
+        $this->assertEquals([2, 3, 4, 13], $this->_model->getCategoryIds());
     }
 
     public function testGetCategoryCollection()
@@ -132,7 +132,7 @@ class ProductExternalTest extends \PHPUnit_Framework_TestCase
         foreach ($fixtureCollection as $category) {
             $ids[] = $category->getId();
         }
-        $this->assertEquals([2, 3, 4], $ids);
+        $this->assertEquals([2, 3, 4, 13], $ids);
     }
 
     public function testGetWebsiteIds()
@@ -342,7 +342,7 @@ class ProductExternalTest extends \PHPUnit_Framework_TestCase
         $actualCategoryIds = $this->_model->getAvailableInCategories();
         sort($actualCategoryIds);
         // not depend on the order of items
-        $this->assertEquals([10, 11, 12], $actualCategoryIds);
+        $this->assertEquals([10, 11, 12, 13], $actualCategoryIds);
         //Check not visible product
         $this->_model->load(
             $this->productRepository->get('simple-3')->getId()
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php
index df15c30672c5471668bd850d446f4d00e497a42c..066e64a2ac81052b2208dd8b79519d5c2f77f7f7 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php
@@ -165,6 +165,20 @@ $category->setId(12)
     ->setPosition(8)
     ->save();
 
+$category = $objectManager->create('Magento\Catalog\Model\Category');
+$category->isObjectNew(true);
+$category->setId(13)
+    ->setName('Category 1.2')
+    ->setParentId(3)
+    ->setPath('1/2/3/13')
+    ->setLevel(3)
+    ->setAvailableSortBy('name')
+    ->setDefaultSortBy('name')
+    ->setIsActive(true)
+    ->setIsAnchor(true)
+    ->setPosition(2)
+    ->save();
+
 /** @var $product \Magento\Catalog\Model\Product */
 $product = $objectManager->create('Magento\Catalog\Model\Product');
 $product->isObjectNew(true);
@@ -183,7 +197,7 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
 
 $categoryLinkManagement->assignProductToCategories(
     $product->getSku(),
-    [2, 3, 4]
+    [2, 3, 4, 13]
 );
 
 $product = $objectManager->create('Magento\Catalog\Model\Product');
@@ -203,7 +217,7 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
 
 $categoryLinkManagement->assignProductToCategories(
     $product->getSku(),
-    [5]
+    [5, 4]
 );
 
 $product = $objectManager->create('Magento\Catalog\Model\Product');
@@ -244,5 +258,5 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
 
 $categoryLinkManagement->assignProductToCategories(
     $product->getSku(),
-    [10, 11, 12]
+    [10, 11, 12, 13]
 );
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/CategoryTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/CategoryTest.php
index 52931492226fd0accba927552a97474c3d1adfe1..6f3ef297d0a57aa856327519d875addf5e339305 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/CategoryTest.php
@@ -106,26 +106,31 @@ class CategoryTest extends \PHPUnit_Framework_TestCase
     {
         $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
         $request = $objectManager->get('Magento\TestFramework\Request');
-        $request->setParam('cat', 4);
+        $request->setParam('cat', 3);
         $this->_model->apply($request);
 
         /** @var $category \Magento\Catalog\Model\Category */
         $category = $objectManager->get('Magento\Framework\Registry')->registry(self::CURRENT_CATEGORY_FILTER);
         $this->assertInstanceOf('Magento\Catalog\Model\Category', $category);
-        $this->assertEquals(4, $category->getId());
+        $this->assertEquals(3, $category->getId());
 
         $items = $this->_model->getItems();
 
         $this->assertInternalType('array', $items);
-        $this->assertEquals(1, count($items));
+        $this->assertEquals(2, count($items));
 
         /** @var $item \Magento\Catalog\Model\Layer\Filter\Item */
         $item = $items[0];
-
         $this->assertInstanceOf('Magento\Catalog\Model\Layer\Filter\Item', $item);
         $this->assertSame($this->_model, $item->getFilter());
-        $this->assertEquals('Category 1.1.1', $item->getLabel());
-        $this->assertEquals(5, $item->getValue());
-        $this->assertEquals(1, $item->getCount());
+        $this->assertEquals('Category 1.1', $item->getLabel());
+        $this->assertEquals(4, $item->getValue());
+        $this->assertEquals(2, $item->getCount());
+
+        $item = $items[1];
+        $this->assertInstanceOf('Magento\Catalog\Model\Layer\Filter\Item', $item);
+        $this->assertEquals('Category 1.2', $item->getLabel());
+        $this->assertEquals(13, $item->getValue());
+        $this->assertEquals(2, $item->getCount());
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php
index 86754a471fd4b1504fbb7c1b43389a12a2da32c4..27d196bb59e7b40058ea30f28bc05aa4eff59fcb 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php
@@ -46,12 +46,13 @@ class AttributesTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetAttributesMeta()
     {
-        $meta = $this->dataProvider->getAttributesMeta($this->entityType);
-        $this->assertArrayHasKey('url_key', $meta);
-        $this->assertEquals('text', $meta['url_key']['dataType']);
-        $this->assertEquals('input', $meta['url_key']['formElement']);
-        $this->assertEquals('1', $meta['url_key']['visible']);
-        $this->assertEquals('0', $meta['url_key']['required']);
-        $this->assertEquals('[STORE VIEW]', $meta['url_key']['scope_label']);
+        $meta = $this->dataProvider->getMeta();
+        $this->assertArrayHasKey('url_key', $meta['search_engine_optimization']['children']);
+        $urlKeyData = $meta['search_engine_optimization']['children']['url_key']['arguments']['data']['config'];
+        $this->assertEquals('text', $urlKeyData['dataType']);
+        $this->assertEquals('input', $urlKeyData['formElement']);
+        $this->assertEquals('1', $urlKeyData['visible']);
+        $this->assertEquals('0', $urlKeyData['required']);
+        $this->assertEquals('[STORE VIEW]', $urlKeyData['scopeLabel']);
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/Product/Type/ConfigurableTest.php
index 3bf0ad712e9d87190e8d7d2ecc33cddef518a34d..5f510ff8f58d67dcf7068845e0f21726e605f59b 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/Product/Type/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/Product/Type/ConfigurableTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\ConfigurableImportExport\Model\Import\Product\Type;
 
+use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\Framework\App\Bootstrap;
 use Magento\Framework\App\Filesystem\DirectoryList;
 use Magento\ImportExport\Model\Import;
@@ -92,8 +93,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         $productId = $resource->getIdBySku(self::TEST_PRODUCT_NAME);
         $this->assertTrue(is_numeric($productId));
         /** @var \Magento\Catalog\Model\Product $product */
-        $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
-        $product->load($productId);
+        $product = $this->objectManager->get(ProductRepositoryInterface::class)->getById($productId);
 
         $this->assertFalse($product->isObjectNew());
         $this->assertEquals(self::TEST_PRODUCT_NAME, $product->getName());
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 a20c3e7b9ab0e4d38fa9482a01b39f7ed23fe29c..6dfe109ee431fbba639abac20418575395e029ad 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
@@ -128,7 +128,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         $attributeId = (int)$testConfigurable->getId();
         $attributes = $this->model->getUsedProductAttributes($this->product);
         $this->assertArrayHasKey($attributeId, $attributes);
-        $this->assertSame($testConfigurable, $attributes[$attributeId]);
+        $this->assertEquals($testConfigurable->getData(), $attributes[$attributeId]->getData());
     }
 
     public function testGetConfigurableAttributes()
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample
index 9074f2a8e0926e210c42a3853daff74537e5d237..af5b2e39078ab5f2fb77f2269e3db905cd8ce3e3 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample
+++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample
@@ -56,7 +56,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa
      */
     public function __sleep()
     {
-        return array('_subject', '_isShared');
+        return ['_subject', '_isShared', '_instanceName'];
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php
index be4608b73fd656b953ceb49221950e808ee336fa..19d117c855f42c849afb643d90fc5d96df236c00 100644
--- a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php
+++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php
@@ -26,12 +26,6 @@ $product->setTypeId('simple')
     ->setName('Simple Product')
     ->setSku('simple')
     ->setPrice(10)
-    ->setStockData([
-    'use_config_manage_stock' => 1,
-    'qty' => 100,
-    'is_qty_decimal' => 0,
-    'is_in_stock' => 100,
-])
     ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
     ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
     ->save();
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php
index c89043d6345db606a24d0f9f0878b495ba5b36dc..f265f82244f19787e9f483e4cef95822aec1aebf 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php
@@ -12,7 +12,6 @@ $product->setTypeId('simple')
     ->setSku('simple')
     ->setPrice(10)
     ->setTaxClassId(0)
-    ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
     ->setMetaTitle('meta title')
     ->setMetaKeyword('meta keyword')
     ->setMetaDescription('meta description')
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
index 1bf0dfb280ed80d164b7638c30f7d6e0697a8bd7..9863052b4f26b980196969b2a621c67a9da4ce26 100755
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
@@ -4129,6 +4129,7 @@ return [
         'Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Media',
         'Magento\Catalog\Model\ResourceModel\Product\Gallery'
     ],
+    ['Magento\CatalogInventory\Observer\AddStockStatusToCollectionObserver'],
     ['Magento\CatalogRule\Block\Adminhtml\Promo\Catalog\Edit\Tab\Actions'],
     ['Magento\CatalogRule\Block\Adminhtml\Promo\Catalog\Edit\Tab\Main'],
     ['Magento\CatalogRule\Block\Adminhtml\Promo\Catalog\Edit\Form'],
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php
index 1cca1b9c94fce2c29f81a297d039dfa97c374f29..38c51080c2086b77c352fdf522deea9b9ad4e47f 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php
@@ -75,7 +75,6 @@ return [
     ['Magento\Core\Model\Resource\Config', 'Magento\Config\Model\ResourceModel\Config'],
     ['Magento\Backend\Block\System\Config', 'Magento\Config\Block\System\Config'],
     ['Magento\Backend\Controller\Adminhtml\System\Config', 'Magento\Config\Controller\Adminhtml\System\Config'],
-    ['Magento\Backend\Model\Config', 'Magento\Config\Model\Config'],
     ['Magento\Core\Model\Variable', 'Magento\Variable\Model\Variable'],
     ['Magento\Catalog\Service'],
     ['Magento\CheckoutAgreements\Service'],
diff --git a/lib/internal/Magento/Framework/AclFactory.php b/lib/internal/Magento/Framework/AclFactory.php
index 3e9839830049d1ec5d384e673e67a64a1de0e866..a48000fbdd33c80c5de69a42c2329f7675c2f984 100644
--- a/lib/internal/Magento/Framework/AclFactory.php
+++ b/lib/internal/Magento/Framework/AclFactory.php
@@ -31,6 +31,6 @@ class AclFactory
      */
     public function create()
     {
-        return $this->_objectManager->create('Magento\Framework\Acl');
+        return $this->_objectManager->create(Acl::class);
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Config/Value.php b/lib/internal/Magento/Framework/App/Config/Value.php
index 8489ffd5c5f28f50cf68aab8196445f9fc9971af..dc09a05efc9d776514ae82e143a60b6fb3b41851 100644
--- a/lib/internal/Magento/Framework/App/Config/Value.php
+++ b/lib/internal/Magento/Framework/App/Config/Value.php
@@ -122,4 +122,18 @@ class Value extends \Magento\Framework\Model\AbstractModel implements \Magento\F
 
         return parent::afterSave();
     }
+
+    /**
+     * {@inheritdoc}
+     *
+     * {@inheritdoc}. In addition, it sets status 'invalidate' for config caches
+     *
+     * @return $this
+     */
+    public function afterDelete()
+    {
+        $this->cacheTypeList->invalidate(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER);
+
+        return parent::afterDelete();
+    }
 }
diff --git a/lib/internal/Magento/Framework/App/PageCache/Cache.php b/lib/internal/Magento/Framework/App/PageCache/Cache.php
index 7cb801730b1fe34b1f2a1e395a6371151ef95b44..9e58ed64d2c0adc75e345d80fc0e77f5eb0ec5d5 100644
--- a/lib/internal/Magento/Framework/App/PageCache/Cache.php
+++ b/lib/internal/Magento/Framework/App/PageCache/Cache.php
@@ -7,11 +7,15 @@ namespace Magento\Framework\App\PageCache;
 
 /**
  * Cache model for builtin cache
+ *
+ * @deprecated
  */
 class Cache extends \Magento\Framework\App\Cache
 {
     /**
      * @var string
+     *
+     * @deprecated
      */
     protected $_frontendIdentifier = 'page_cache';
 }
diff --git a/lib/internal/Magento/Framework/App/PageCache/Kernel.php b/lib/internal/Magento/Framework/App/PageCache/Kernel.php
index b065336108b9a985dd090650500254d94f16a03c..587e8100b5f97f35a20d1c933f0b92da945fe5d2 100644
--- a/lib/internal/Magento/Framework/App/PageCache/Kernel.php
+++ b/lib/internal/Magento/Framework/App/PageCache/Kernel.php
@@ -5,13 +5,17 @@
  */
 namespace Magento\Framework\App\PageCache;
 
+use Magento\Framework\App\ObjectManager;
+
 /**
  * Builtin cache processor
  */
 class Kernel
 {
     /**
-     * @var Cache
+     * @var \Magento\PageCache\Model\Cache\Type
+     *
+     * @deprecated
      */
     protected $cache;
 
@@ -25,6 +29,11 @@ class Kernel
      */
     protected $request;
 
+    /**
+     * @var \Magento\PageCache\Model\Cache\Type
+     */
+    private $fullPageCache;
+
     /**
      * @param Cache $cache
      * @param Identifier $identifier
@@ -48,7 +57,7 @@ class Kernel
     public function load()
     {
         if ($this->request->isGet() || $this->request->isHead()) {
-            return unserialize($this->cache->load($this->identifier->getValue()));
+            return unserialize($this->getCache()->load($this->identifier->getValue()));
         }
         return false;
     }
@@ -75,8 +84,21 @@ class Kernel
                 if (!headers_sent()) {
                     header_remove('Set-Cookie');
                 }
-                $this->cache->save(serialize($response), $this->identifier->getValue(), $tags, $maxAge);
+                $this->getCache()->save(serialize($response), $this->identifier->getValue(), $tags, $maxAge);
             }
         }
     }
+
+    /**
+     * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547
+     *
+     * @return \Magento\PageCache\Model\Cache\Type
+     */
+    private function getCache()
+    {
+        if (!$this->fullPageCache) {
+            $this->fullPageCache = ObjectManager::getInstance()->get('\Magento\PageCache\Model\Cache\Type');
+        }
+        return $this->fullPageCache;
+    }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Config/ValueTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Config/ValueTest.php
index 175509af622a025d5b05b879e227f763cd3a3f56..be5dccf9920f83bfed4403f482bcda53579b8006 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Config/ValueTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Config/ValueTest.php
@@ -191,4 +191,13 @@ class ValueTest extends \PHPUnit_Framework_TestCase
             [1, 'other_value'],
         ];
     }
+
+    /**
+     * @return void;
+     */
+    public function testAfterDelete()
+    {
+        $this->cacheTypeListMock->expects($this->once())->method('invalidate');
+        $this->assertInstanceOf(get_class($this->model), $this->model->afterDelete());
+    }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php
index 1028112e36aefb5e92a9d62af8bf126caa6d5121..070223bf8ff3b6d2578db75b9baad8871a138d4d 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php
@@ -9,41 +9,41 @@ use \Magento\Framework\App\PageCache\Kernel;
 
 class KernelTest extends \PHPUnit_Framework_TestCase
 {
-    /**
-     * @var Kernel
-     */
+    /** @var Kernel */
     protected $kernel;
 
-    /**
-     * @var \Magento\Framework\App\PageCache\Cache|\PHPUnit_Framework_MockObject_MockObject
-     */
+    /** @var \Magento\Framework\App\PageCache\Cache|\PHPUnit_Framework_MockObject_MockObject */
     protected $cacheMock;
 
-    /**
-     * @var \Magento\Framework\App\PageCache\Identifier|\PHPUnit_Framework_MockObject_MockObject
-     */
+    /** @var \Magento\Framework\App\PageCache\Identifier|\PHPUnit_Framework_MockObject_MockObject */
     protected $identifierMock;
 
-    /**
-     * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject
-     */
+    /** @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject */
     protected $requestMock;
 
-    /**
-     * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject
-     */
+    /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject */
     protected $responseMock;
 
+    /** @var  \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */
+    private $fullPageCacheMock;
+
     /**
      * Setup
      */
     public function setUp()
     {
         $this->cacheMock = $this->getMock('Magento\Framework\App\PageCache\Cache', [], [], '', false);
+        $this->fullPageCacheMock = $this->getMock('\Magento\PageCache\Model\Cache\Type', [], [], '', false);
         $this->identifierMock =
             $this->getMock('Magento\Framework\App\PageCache\Identifier', [], [], '', false);
         $this->requestMock = $this->getMock('Magento\Framework\App\Request\Http', [], [], '', false);
         $this->kernel = new Kernel($this->cacheMock, $this->identifierMock, $this->requestMock);
+
+        $reflection = new \ReflectionClass('\Magento\Framework\App\PageCache\Kernel');
+        $reflectionProperty = $reflection->getProperty('fullPageCache');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($this->kernel, $this->fullPageCacheMock);
+
         $this->responseMock = $this->getMockBuilder(
             'Magento\Framework\App\Response\Http'
         )->setMethods(
@@ -63,7 +63,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase
     {
         $this->requestMock->expects($this->once())->method('isGet')->will($this->returnValue($isGet));
         $this->requestMock->expects($this->any())->method('isHead')->will($this->returnValue($isHead));
-        $this->cacheMock->expects(
+        $this->fullPageCacheMock->expects(
             $this->any()
         )->method(
             'load'
@@ -136,7 +136,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase
         $this->responseMock->expects($this->at($at[2]))
             ->method('clearHeader')
             ->with($this->equalTo('X-Magento-Tags'));
-        $this->cacheMock->expects($this->once())
+        $this->fullPageCacheMock->expects($this->once())
             ->method('save');
         $this->kernel->process($this->responseMock);
     }
@@ -173,7 +173,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase
         if ($overrideHeaders) {
             $this->responseMock->expects($this->once())->method('setNoCacheHeaders');
         }
-        $this->cacheMock->expects($this->never())->method('save');
+        $this->fullPageCacheMock->expects($this->never())->method('save');
         $this->kernel->process($this->responseMock);
     }
 
diff --git a/lib/internal/Magento/Framework/Cache/Backend/Database.php b/lib/internal/Magento/Framework/Cache/Backend/Database.php
index f02c9975ab4b87fc73b6fde8180a3a082a08e8c2..4ed3a7be309e6ec5fb1c67ea14aa0a3eb6d16ef8 100644
--- a/lib/internal/Magento/Framework/Cache/Backend/Database.php
+++ b/lib/internal/Magento/Framework/Cache/Backend/Database.php
@@ -48,6 +48,7 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
         'tags_table' => '',
         'tags_table_callback' => '',
         'store_data' => true,
+        'infinite_loop_flag' => false,
     ];
 
     /**
@@ -145,13 +146,16 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
      */
     public function load($id, $doNotTestCacheValidity = false)
     {
-        if ($this->_options['store_data']) {
+        if ($this->_options['store_data'] && !$this->_options['infinite_loop_flag']) {
+            $this->_options['infinite_loop_flag'] = true;
             $select = $this->_getConnection()->select()->from($this->_getDataTable(), 'data')->where('id=:cache_id');
 
             if (!$doNotTestCacheValidity) {
                 $select->where('expire_time=0 OR expire_time>?', time());
             }
-            return $this->_getConnection()->fetchOne($select, ['cache_id' => $id]);
+            $result = $this->_getConnection()->fetchOne($select, ['cache_id' => $id]);
+            $this->_options['infinite_loop_flag'] = false;
+            return $result;
         } else {
             return false;
         }
@@ -165,7 +169,8 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
      */
     public function test($id)
     {
-        if ($this->_options['store_data']) {
+        if ($this->_options['store_data'] && !$this->_options['infinite_loop_flag']) {
+            $this->_options['infinite_loop_flag'] = true;
             $select = $this->_getConnection()->select()->from(
                 $this->_getDataTable(),
                 'update_time'
@@ -175,7 +180,9 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
                 'expire_time=0 OR expire_time>?',
                 time()
             );
-            return $this->_getConnection()->fetchOne($select, ['cache_id' => $id]);
+            $result = $this->_getConnection()->fetchOne($select, ['cache_id' => $id]);
+            $this->_options['infinite_loop_flag'] = false;
+            return $result;
         } else {
             return false;
         }
@@ -195,17 +202,21 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
      */
     public function save($data, $id, $tags = [], $specificLifetime = false)
     {
-        if ($this->_options['store_data']) {
-            $connection = $this->_getConnection();
-            $dataTable = $this->_getDataTable();
+        $result = false;
+        if (!$this->_options['infinite_loop_flag']) {
+            $this->_options['infinite_loop_flag'] = true;
+            $result = true;
+            if ($this->_options['store_data']) {
+                $connection = $this->_getConnection();
+                $dataTable = $this->_getDataTable();
 
-            $lifetime = $this->getLifetime($specificLifetime);
-            $time = time();
-            $expire = $lifetime === 0 || $lifetime === null ? 0 : $time + $lifetime;
+                $lifetime = $this->getLifetime($specificLifetime);
+                $time = time();
+                $expire = $lifetime === 0 || $lifetime === null ? 0 : $time + $lifetime;
 
-            $dataCol = $connection->quoteIdentifier('data');
-            $expireCol = $connection->quoteIdentifier('expire_time');
-            $query = "INSERT INTO {$dataTable} (\n                    {$connection->quoteIdentifier(
+                $dataCol = $connection->quoteIdentifier('data');
+                $expireCol = $connection->quoteIdentifier('expire_time');
+                $query = "INSERT INTO {$dataTable} (\n                    {$connection->quoteIdentifier(
                 'id'
             )},\n                    {$dataCol},\n                    {$connection->quoteIdentifier(
                 'create_time'
@@ -213,13 +224,14 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
                 'update_time'
             )},\n                    {$expireCol})\n                VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE\n                    {$dataCol}=VALUES({$dataCol}),\n                    {$expireCol}=VALUES({$expireCol})";
 
-            $result = $connection->query($query, [$id, $data, $time, $time, $expire])->rowCount();
-            if (!$result) {
-                return false;
+                $result = $connection->query($query, [$id, $data, $time, $time, $expire])->rowCount();
             }
+            if ($result) {
+                $result = $this->_saveTags($id, $tags);
+            }
+            $this->_options['infinite_loop_flag'] = false;
         }
-        $tagRes = $this->_saveTags($id, $tags);
-        return $tagRes;
+        return $result;
     }
 
     /**
@@ -230,8 +242,11 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
      */
     public function remove($id)
     {
-        if ($this->_options['store_data']) {
-            return $this->_getConnection()->delete($this->_getDataTable(), ['id=?' => $id]);
+        if ($this->_options['store_data'] && !$this->_options['infinite_loop_flag']) {
+            $this->_options['infinite_loop_flag'] = true;
+            $result = $this->_getConnection()->delete($this->_getDataTable(), ['id=?' => $id]);
+            $this->_options['infinite_loop_flag'] = false;
+            return $result;
         }
         return false;
     }
@@ -255,34 +270,26 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
      */
     public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = [])
     {
-        $connection = $this->_getConnection();
-        switch ($mode) {
-            case \Zend_Cache::CLEANING_MODE_ALL:
-                if ($this->_options['store_data']) {
-                    $result = $connection->query('TRUNCATE TABLE ' . $this->_getDataTable());
-                } else {
-                    $result = true;
-                }
-                $result = $result && $connection->query('TRUNCATE TABLE ' . $this->_getTagsTable());
-                break;
-            case \Zend_Cache::CLEANING_MODE_OLD:
-                if ($this->_options['store_data']) {
-                    $result = $connection->delete(
-                        $this->_getDataTable(),
-                        ['expire_time> ?' => 0, 'expire_time<= ?' => time()]
-                    );
-                } else {
-                    $result = true;
-                }
-                break;
-            case \Zend_Cache::CLEANING_MODE_MATCHING_TAG:
-            case \Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
-            case \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
-                $result = $this->_cleanByTags($mode, $tags);
-                break;
-            default:
-                \Zend_Cache::throwException('Invalid mode for clean() method');
-                break;
+        if (!$this->_options['infinite_loop_flag']) {
+            $this->_options['infinite_loop_flag'] = true;
+            $connection = $this->_getConnection();
+            switch ($mode) {
+                case \Zend_Cache::CLEANING_MODE_ALL:
+                    $result = $this->cleanAll($connection);
+                    break;
+                case \Zend_Cache::CLEANING_MODE_OLD:
+                    $result = $this->cleanOld($connection);
+                    break;
+                case \Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+                case \Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+                case \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+                    $result = $this->_cleanByTags($mode, $tags);
+                    break;
+                default:
+                    \Zend_Cache::throwException('Invalid mode for clean() method');
+                    break;
+            }
+            $this->_options['infinite_loop_flag'] = false;
         }
 
         return $result;
@@ -543,4 +550,41 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend
             return true;
         }
     }
+
+    /**
+     * Clean all cache entries
+     *
+     * @param $connection
+     * @return bool
+     */
+    private function cleanAll($connection)
+    {
+        if ($this->_options['store_data']) {
+            $result = $connection->query('TRUNCATE TABLE ' . $this->_getDataTable());
+        } else {
+            $result = true;
+        }
+        $result = $result && $connection->query('TRUNCATE TABLE ' . $this->_getTagsTable());
+        return $result;
+    }
+
+    /**
+     * Clean old cache entries
+     *
+     * @param $connection
+     * @return bool
+     */
+    private function cleanOld($connection)
+    {
+        if ($this->_options['store_data']) {
+            $result = $connection->delete(
+                $this->_getDataTable(),
+                ['expire_time> ?' => 0, 'expire_time<= ?' => time()]
+            );
+            return $result;
+        } else {
+            $result = true;
+            return $result;
+        }
+    }
 }
diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php
index 44258c7f135ce0e122f3a63d6f0fe1fd26737fe4..fd122da9985ee9fee355113bf59c5a00ac373ae7 100644
--- a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php
+++ b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php
@@ -46,7 +46,10 @@ class ComponentRegistrar implements ComponentRegistrarInterface
     {
         self::validateType($type);
         if (isset(self::$paths[$type][$componentName])) {
-            throw new \LogicException('\'' . $componentName . '\' component already exists');
+            throw new \LogicException(
+                ucfirst($type) . ' \'' . $componentName . '\' from \'' . $path . '\' '
+                . 'has been already defined in \'' . self::$paths[$type][$componentName] . '\'.'
+            );
         } else {
             self::$paths[$type][$componentName] = str_replace('\\', '/', $path);
         }
diff --git a/lib/internal/Magento/Framework/Component/Test/Unit/ComponentRegistrarTest.php b/lib/internal/Magento/Framework/Component/Test/Unit/ComponentRegistrarTest.php
index db7fa8f35e8c5eafdf6f7a797654a8947483c832..93f6983ce237aaaae81d22ce5a044ca356c55057 100644
--- a/lib/internal/Magento/Framework/Component/Test/Unit/ComponentRegistrarTest.php
+++ b/lib/internal/Magento/Framework/Component/Test/Unit/ComponentRegistrarTest.php
@@ -45,11 +45,11 @@ class ComponentRegistrarTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \LogicException
-     * @expectedExceptionMessage 'test_module_one' component already exists
+     * @expectedExceptionMessageRegExp /Module 'test_module_one' from '\w+' has been already defined in '\w+'./
      */
     public function testRegistrarWithExceptionForModules()
     {
-        ComponentRegistrar::register(ComponentRegistrar::MODULE, "test_module_one", "some/path/name/one");
+        ComponentRegistrar::register(ComponentRegistrar::MODULE, "test_module_one", "some/path/name/onemore");
     }
 
     public function testGetPath()
diff --git a/lib/internal/Magento/Framework/Controller/ResultFactory.php b/lib/internal/Magento/Framework/Controller/ResultFactory.php
index db78cf29c63014d7102ef03cabd1378fd6ee828d..1543368a9dd82985428f002efac6a01ae5f255f6 100644
--- a/lib/internal/Magento/Framework/Controller/ResultFactory.php
+++ b/lib/internal/Magento/Framework/Controller/ResultFactory.php
@@ -30,12 +30,12 @@ class ResultFactory
      * @var array
      */
     protected $typeMap = [
-        self::TYPE_JSON     => 'Magento\Framework\Controller\Result\Json',
-        self::TYPE_RAW      => 'Magento\Framework\Controller\Result\Raw',
-        self::TYPE_REDIRECT => 'Magento\Framework\Controller\Result\Redirect',
-        self::TYPE_FORWARD  => 'Magento\Framework\Controller\Result\Forward',
-        self::TYPE_LAYOUT   => 'Magento\Framework\View\Result\Layout',
-        self::TYPE_PAGE     => 'Magento\Framework\View\Result\Page',
+        self::TYPE_JSON     => Result\Json::class,
+        self::TYPE_RAW      => Result\Raw::class,
+        self::TYPE_REDIRECT => Result\Redirect::class,
+        self::TYPE_FORWARD  => Result\Forward::class,
+        self::TYPE_LAYOUT   => \Magento\Framework\View\Result\Layout::class,
+        self::TYPE_PAGE     => \Magento\Framework\View\Result\Page::class,
     ];
 
     /**
diff --git a/lib/internal/Magento/Framework/CurrencyFactory.php b/lib/internal/Magento/Framework/CurrencyFactory.php
index a1a126aecd1f99f568e00437842cd9e652184fb6..1f87858182d0e3b2fb2e94cddb4c50f24c7d9691 100644
--- a/lib/internal/Magento/Framework/CurrencyFactory.php
+++ b/lib/internal/Magento/Framework/CurrencyFactory.php
@@ -11,7 +11,7 @@ namespace Magento\Framework;
 class CurrencyFactory
 {
     /**
-     * @var \Magento\Framework\ObjectManagerInterface
+     * @var ObjectManagerInterface
      */
     protected $_objectManager = null;
 
@@ -21,10 +21,10 @@ class CurrencyFactory
     protected $_instanceName = null;
 
     /**
-     * @param \Magento\Framework\ObjectManagerInterface $objectManager
+     * @param ObjectManagerInterface $objectManager
      * @param string $instanceName
      */
-    public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = 'Magento\Framework\CurrencyInterface')
+    public function __construct(ObjectManagerInterface $objectManager, $instanceName = CurrencyInterface::class)
     {
         $this->_objectManager = $objectManager;
         $this->_instanceName = $instanceName;
@@ -34,7 +34,7 @@ class CurrencyFactory
      * Create class instance with specified parameters
      *
      * @param array $data
-     * @return \Magento\Framework\CurrencyInterface
+     * @return CurrencyInterface
      */
     public function create(array $data = [])
     {
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index 75abe8a90ed85df29a5ff6edcff0246e08424921..913dc4a4a92e7d7dace42fed2ff89447ffeabfe9 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Framework\DB;
 
+use Magento\Framework\App\ResourceConnection;
 use Magento\Framework\DB\Adapter\AdapterInterface;
 
 /**
@@ -501,4 +502,32 @@ class Select extends \Zend_Db_Select
     {
         return $this->selectRenderer->render($this);
     }
+
+    /**
+     * @return string[]
+     */
+    public function __sleep()
+    {
+        $properties = array_keys(get_object_vars($this));
+        $properties = array_diff(
+            $properties,
+            [
+                '_adapter',
+                'selectRenderer'
+            ]
+        );
+        return $properties;
+    }
+
+    /**
+     * Init not serializable fields
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_adapter = $objectManager->get(ResourceConnection::class)->getConnection();
+        $this->selectRenderer = $objectManager->get(\Magento\Framework\DB\Select\SelectRenderer::class);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index a4cf35da2a1de5063bbe73dbb5aecbac4a491431..ef0416419d18ca8a93b01a2a20be6235b4a35c0e 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -866,4 +866,30 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll
     {
         return array_key_exists($flag, $this->_flags);
     }
+
+    /**
+     * @return string[]
+     */
+    public function __sleep()
+    {
+        $properties = array_keys(get_object_vars($this));
+        $properties = array_diff(
+            $properties,
+            [
+                '_entityFactory',
+            ]
+        );
+        return $properties;
+    }
+
+    /**
+     * Init not serializable fields
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_entityFactory = $objectManager->get(EntityFactoryInterface::class);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
index b35881d0132767be0e28c9d9fd62fc99c99637f8..dc2b8de38ab12642791ed106d81d91da9f63438a 100644
--- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Framework\Data\Collection;
 
+use Magento\Framework\App\ResourceConnection;
 use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
 use Magento\Framework\DB\Adapter\AdapterInterface;
 use Magento\Framework\DB\Select;
@@ -880,4 +881,27 @@ abstract class AbstractDb extends \Magento\Framework\Data\Collection
         }
         throw new \LogicException("Main table cannot be identified.");
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            ['_fetchStrategy', '_logger', '_conn', 'extensionAttributesJoinProcessor']
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_fetchStrategy = $objectManager->get(Logger::class);
+        $this->_logger = $objectManager->get(FetchStrategyInterface::class);
+        $this->_conn = $objectManager->get(ResourceConnection::class)->getConnection();
+    }
 }
diff --git a/lib/internal/Magento/Framework/DataObject/IdentityInterface.php b/lib/internal/Magento/Framework/DataObject/IdentityInterface.php
index 9d8ab0169388e5a4a8ba3ce2e7043c01cb2973a1..614ca594e3c9d331b04bf299d2130470c3e8ddfd 100644
--- a/lib/internal/Magento/Framework/DataObject/IdentityInterface.php
+++ b/lib/internal/Magento/Framework/DataObject/IdentityInterface.php
@@ -15,7 +15,7 @@ interface IdentityInterface
     /**
      * Return unique ID(s) for each object in system
      *
-     * @return array
+     * @return string[]
      */
     public function getIdentities();
 }
diff --git a/lib/internal/Magento/Framework/EventFactory.php b/lib/internal/Magento/Framework/EventFactory.php
index 47433049bac1b5f03c13407f7bbb468d9cec5dde..e73cb8b96a0941156975523d44924318cb124016 100644
--- a/lib/internal/Magento/Framework/EventFactory.php
+++ b/lib/internal/Magento/Framework/EventFactory.php
@@ -26,6 +26,6 @@ class EventFactory
      */
     public function create($arguments = [])
     {
-        return $this->_objectManager->create('Magento\Framework\Event', $arguments);
+        return $this->_objectManager->create(Event::class, $arguments);
     }
 }
diff --git a/lib/internal/Magento/Framework/FlagFactory.php b/lib/internal/Magento/Framework/FlagFactory.php
index 99aa8d0047d19c9e85030a15b173c724bb53d77b..fcd066591a061b50fb865ec97c6435395008da1f 100644
--- a/lib/internal/Magento/Framework/FlagFactory.php
+++ b/lib/internal/Magento/Framework/FlagFactory.php
@@ -32,7 +32,7 @@ class FlagFactory
      */
     public function __construct(
         \Magento\Framework\ObjectManagerInterface $objectManager,
-        $instanceName = 'Magento\Framework\Flag'
+        $instanceName = Flag::class
     ) {
         $this->_objectManager = $objectManager;
         $this->_instanceName = $instanceName;
diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php
index 1fcf8e4134812d81e65fbc3844cb3b63b4331e31..1c13c0c3b0bc83104e5bc353b32f1679ccfc37ff 100644
--- a/lib/internal/Magento/Framework/Interception/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Interceptor.php
@@ -82,10 +82,12 @@ trait Interceptor
     public function __sleep()
     {
         if (method_exists(get_parent_class($this), '__sleep')) {
-            return array_diff(parent::__sleep(), ['pluginLocator', 'pluginList', 'chain', 'subjectType']);
+            $properties = parent::__sleep();
         } else {
-            return array_keys(get_class_vars(get_parent_class($this)));
+            $properties = array_keys(get_object_vars($this));
         }
+        $properties = array_diff($properties, ['pluginLocator', 'pluginList', 'chain', 'subjectType', 'pluginLocator']);
+        return $properties;
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php
index a1a7a29ba3313b4cd45128281cc7bae8d5320590..3bbf7a2f9743aa0ebe250c4be8e2f64581f81bad 100644
--- a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php
+++ b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php
@@ -17,7 +17,6 @@ use Magento\Framework\Interception\ObjectManager\ConfigInterface;
 use Magento\Framework\ObjectManager\RelationsInterface;
 use Magento\Framework\ObjectManager\DefinitionInterface as ClassDefinitions;
 use Magento\Framework\ObjectManagerInterface;
-use Zend\Soap\Exception\InvalidArgumentException;
 
 /**
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -115,7 +114,7 @@ class PluginList extends Scoped implements InterceptionPluginList
      *
      * @param string $type
      * @return array
-     * @throws InvalidArgumentException
+     * @throws \InvalidArgumentException
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      * @SuppressWarnings(PHPMD.NPathComplexity)
      */
@@ -162,7 +161,7 @@ class PluginList extends Scoped implements InterceptionPluginList
                     }
                     $pluginType = $this->_omConfig->getOriginalInstanceType($plugin['instance']);
                     if (!class_exists($pluginType)) {
-                        throw new InvalidArgumentException('Plugin class ' . $pluginType . ' doesn\'t exist');
+                        throw new \InvalidArgumentException('Plugin class ' . $pluginType . ' doesn\'t exist');
                     }
                     foreach ($this->_definitions->getMethodList($pluginType) as $pluginMethod => $methodTypes) {
                         $current = isset($lastPerMethod[$pluginMethod]) ? $lastPerMethod[$pluginMethod] : '__self';
diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
index 24802ef1ac110d72e91c1af0e40c527843dce481..7be929ccc5a7e6362a97fcf8daedef27d6b3a36e 100644
--- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
@@ -7,6 +7,7 @@
 namespace Magento\Framework\Model;
 
 use Magento\Framework\Api\AttributeValueFactory;
+use Magento\Framework\Api\ExtensionAttributesFactory;
 
 /**
  * Abstract model with custom attributes support.
@@ -19,7 +20,7 @@ abstract class AbstractExtensibleModel extends AbstractModel implements
     \Magento\Framework\Api\CustomAttributesDataInterface
 {
     /**
-     * @var \Magento\Framework\Api\ExtensionAttributesFactory
+     * @var ExtensionAttributesFactory
      */
     protected $extensionAttributesFactory;
 
@@ -46,7 +47,7 @@ abstract class AbstractExtensibleModel extends AbstractModel implements
     /**
      * @param \Magento\Framework\Model\Context $context
      * @param \Magento\Framework\Registry $registry
-     * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
+     * @param ExtensionAttributesFactory $extensionFactory
      * @param AttributeValueFactory $customAttributeFactory
      * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
      * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
@@ -55,7 +56,7 @@ abstract class AbstractExtensibleModel extends AbstractModel implements
     public function __construct(
         \Magento\Framework\Model\Context $context,
         \Magento\Framework\Registry $registry,
-        \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
+        ExtensionAttributesFactory $extensionFactory,
         AttributeValueFactory $customAttributeFactory,
         \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
         \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
@@ -328,4 +329,23 @@ abstract class AbstractExtensibleModel extends AbstractModel implements
     {
         return $this->getData(self::EXTENSION_ATTRIBUTES_KEY);
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(parent::__sleep(), ['extensionAttributesFactory', 'customAttributeFactory']);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->extensionAttributesFactory = $objectManager->get(ExtensionAttributesFactory::class);
+        $this->customAttributeFactory = $objectManager->get(AttributeValueFactory::class);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Model/AbstractModel.php b/lib/internal/Magento/Framework/Model/AbstractModel.php
index f72c455daf860caec4e77dbf71c901832015dec7..d53820eff1855582e5ad01ddcf0e936f05cb8cf1 100644
--- a/lib/internal/Magento/Framework/Model/AbstractModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractModel.php
@@ -220,7 +220,19 @@ abstract class AbstractModel extends \Magento\Framework\DataObject
     public function __sleep()
     {
         $properties = array_keys(get_object_vars($this));
-        $properties = array_diff($properties, ['_eventManager', '_cacheManager', '_registry', '_appState']);
+        $properties = array_diff(
+            $properties,
+            [
+                '_eventManager',
+                '_cacheManager',
+                '_registry',
+                '_appState',
+                '_actionValidator',
+                '_logger',
+                '_resourceCollection',
+                '_resource',
+            ]
+        );
         return $properties;
     }
 
@@ -232,12 +244,15 @@ abstract class AbstractModel extends \Magento\Framework\DataObject
     public function __wakeup()
     {
         $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
-        $this->_eventManager = $objectManager->get('Magento\Framework\Event\ManagerInterface');
-        $this->_cacheManager = $objectManager->get('Magento\Framework\App\CacheInterface');
         $this->_registry = $objectManager->get('Magento\Framework\Registry');
+
         $context = $objectManager->get('Magento\Framework\Model\Context');
         if ($context instanceof \Magento\Framework\Model\Context) {
             $this->_appState = $context->getAppState();
+            $this->_eventManager = $context->getEventDispatcher();
+            $this->_cacheManager = $context->getCacheManager();
+            $this->_logger = $context->getLogger();
+            $this->_actionValidator = $context->getActionValidator();
         }
     }
 
@@ -263,7 +278,6 @@ abstract class AbstractModel extends \Magento\Framework\DataObject
         return $this->_idFieldName;
     }
 
-
     /**
      * Identifier getter
      *
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
index de66653672dd6be913988ea1acd68391c47ec531..578b2c151164182c1d8242c2862fed5003b89ef6 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
@@ -592,4 +592,25 @@ abstract class AbstractCollection extends \Magento\Framework\Data\Collection\Abs
         }
         return $this;
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function __sleep()
+    {
+        return array_diff(
+            parent::__sleep(),
+            ['_resource', '_eventManager']
+        );
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_eventManager = $objectManager->get(\Magento\Framework\Event\ManagerInterface::class);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
index a026f2a60c82e51d076d99b2b7655000c84db58f..15ebfc6a8539aeac634ae57805f7bd356facc2c8 100644
--- a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
@@ -59,7 +59,7 @@ class Proxy extends \Magento\Framework\Mview\Config\Data implements
      */
     public function __sleep()
     {
-        return ['_subject', '_isShared'];
+        return ['subject', 'isShared'];
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php
index c0f86fa70b2bef63efa7896f6b30dc2c228acaa7..442ad8261a403fbe34fb659e2a1f4df8b1a901ca 100644
--- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php
+++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php
@@ -81,7 +81,7 @@ class Proxy extends \Magento\Framework\Code\Generator\EntityAbstract
         $methods = [$construct];
         $methods[] = [
             'name' => '__sleep',
-            'body' => 'return array(\'_subject\', \'_isShared\');',
+            'body' => 'return [\'_subject\', \'_isShared\', \'_instanceName\'];',
             'docblock' => ['tags' => [['name' => 'return', 'description' => 'array']]],
         ];
         $methods[] = [
diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleProxy.txt b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleProxy.txt
index f3fadd9d31b50927ab0d573ead7521c89f2adc1e..43c99aa5e6843e3676a6b47c618022c78e5a9bc4 100644
--- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleProxy.txt
+++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleProxy.txt
@@ -52,7 +52,7 @@ class Sample_Proxy extends \Magento\Framework\ObjectManager\Code\Generator\Sampl
      */
     public function __sleep()
     {
-        return array('_subject', '_isShared');
+        return ['_subject', '_isShared', '_instanceName'];
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php
index 45181680b33ec8ec1767df9c8b720713e5f576e1..a8664e3f10a341809c78fddde828e0b3178b2631 100644
--- a/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php
+++ b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php
@@ -6,6 +6,7 @@
 
 namespace Magento\Framework\Pricing\Render;
 
+use Magento\Framework\DataObject\IdentityInterface;
 use Magento\Framework\Pricing\Amount\AmountInterface;
 use Magento\Framework\Pricing\SaleableInterface;
 use Magento\Framework\Pricing\Price\PriceInterface;
@@ -17,8 +18,11 @@ use Magento\Framework\View\Element\Template;
  * @method bool hasListClass()
  * @method string getListClass()
  */
-class PriceBox extends Template implements PriceBoxRenderInterface
+class PriceBox extends Template implements PriceBoxRenderInterface, IdentityInterface
 {
+    /** Default block lifetime */
+    const DEFAULT_LIFETIME = 3600;
+
     /**
      * @var SaleableInterface
      */
@@ -65,6 +69,26 @@ class PriceBox extends Template implements PriceBoxRenderInterface
         return parent::_toHtml();
     }
 
+    /**
+     * Get Key for caching block content
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        return parent::getCacheKey() . '-' . $this->getPriceId() . '-' . $this->getPrice()->getPriceCode();
+    }
+
+    /**
+     * Get block cache life time
+     *
+     * @return int
+     */
+    protected function getCacheLifetime()
+    {
+        return parent::hasCacheLifetime() ? parent::getCacheLifetime() : self::DEFAULT_LIFETIME;
+    }
+    
     /**
      * @return SaleableInterface
      */
@@ -146,4 +170,19 @@ class PriceBox extends Template implements PriceBoxRenderInterface
     {
         return $this->rendererPool;
     }
+
+    /**
+     * Return unique ID(s) for each object in system
+     *
+     * @return array
+     */
+    public function getIdentities()
+    {
+        $item = $this->getSaleableItem();
+        if ($item instanceof IdentityInterface) {
+            return $item->getIdentities();
+        } else {
+            return [];
+        }
+    }
 }
diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php
index 8ffa2d1749f62a234c97f6da15f573d4e3a00995..9363c9fc0196c2870e60803907d4374c57c08434 100644
--- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php
+++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php
@@ -54,6 +54,8 @@ class PriceBoxTest extends \PHPUnit_Framework_TestCase
         $layout = $this->getMock('Magento\Framework\View\LayoutInterface');
         $eventManager = $this->getMock('Magento\Framework\Event\ManagerInterface');
         $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface');
+        $cacheState = $this->getMockBuilder(\Magento\Framework\App\Cache\StateInterface::class)
+            ->getMockForAbstractClass();
         $storeConfig = $this->getMockBuilder('Magento\Store\Model\Store\Config')
             ->disableOriginalConstructor()
             ->getMock();
@@ -72,6 +74,9 @@ class PriceBoxTest extends \PHPUnit_Framework_TestCase
         $this->context->expects($this->any())
             ->method('getScopeConfig')
             ->will($this->returnValue($scopeConfigMock));
+        $this->context->expects($this->any())
+            ->method('getCacheState')
+            ->will($this->returnValue($cacheState));
 
         $this->saleable = $this->getMock('Magento\Framework\Pricing\SaleableInterface');
 
diff --git a/lib/internal/Magento/Framework/Session/Config.php b/lib/internal/Magento/Framework/Session/Config.php
index 793794b61826e09f088fef985593e40aae9aada5..99f3eb96d773c8dfa34c6f0a6380027c6288e174 100644
--- a/lib/internal/Magento/Framework/Session/Config.php
+++ b/lib/internal/Magento/Framework/Session/Config.php
@@ -18,44 +18,28 @@ use Magento\Framework\Session\SaveHandlerInterface;
  */
 class Config implements ConfigInterface
 {
-    /**
-     * Configuration path for session save method
-     */
+    /** Configuration path for session save method */
     const PARAM_SESSION_SAVE_METHOD = 'session/save';
 
-    /**
-     * Configuration path for session save path
-     */
+    /** Configuration path for session save path */
     const PARAM_SESSION_SAVE_PATH = 'session/save_path';
 
-    /**
-     * Configuration path for session cache limiter
-     */
+    /** Configuration path for session cache limiter */
     const PARAM_SESSION_CACHE_LIMITER = 'session/cache_limiter';
 
-    /**
-     * Configuration path for cookie domain
-     */
+    /** Configuration path for cookie domain */
     const XML_PATH_COOKIE_DOMAIN = 'web/cookie/cookie_domain';
 
-    /**
-     * Configuration path for cookie lifetime
-     */
+    /** Configuration path for cookie lifetime */
     const XML_PATH_COOKIE_LIFETIME = 'web/cookie/cookie_lifetime';
 
-    /**
-     * Configuration path for cookie http only param
-     */
+    /** Configuration path for cookie http only param */
     const XML_PATH_COOKIE_HTTPONLY = 'web/cookie/cookie_httponly';
 
-    /**
-     * Configuration path for cookie path
-     */
+    /** Configuration path for cookie path */
     const XML_PATH_COOKIE_PATH = 'web/cookie/cookie_path';
 
-    /**
-     * Cookie default lifetime
-     */
+    /** Cookie default lifetime */
     const COOKIE_LIFETIME_DEFAULT = 3600;
 
     /**
@@ -65,19 +49,13 @@ class Config implements ConfigInterface
      */
     protected $options = [];
 
-    /**
-     * @var \Magento\Framework\App\Config\ScopeConfigInterface
-     */
+    /** @var \Magento\Framework\App\Config\ScopeConfigInterface */
     protected $_scopeConfig;
 
-    /**
-     * @var \Magento\Framework\Stdlib\StringUtils
-     */
+    /** @var \Magento\Framework\Stdlib\StringUtils */
     protected $_stringHelper;
 
-    /**
-     * @var \Magento\Framework\App\RequestInterface
-     */
+    /** @var \Magento\Framework\App\RequestInterface */
     protected $_httpRequest;
 
     /**
@@ -92,17 +70,16 @@ class Config implements ConfigInterface
         'session.cookie_httponly',
     ];
 
-    /**
-     * @var string
-     */
+    /** @var string */
     protected $_scopeType;
 
-    /**
-     * @var string
-     */
+    /** @var string */
+    protected $lifetimePath;
+
+    /** @var string */
     private $saveHandlerName;
 
-    /** @var  \Magento\Framework\ValidatorFactory */
+    /** @var \Magento\Framework\ValidatorFactory */
     protected $_validatorFactory;
 
     /**
@@ -131,6 +108,7 @@ class Config implements ConfigInterface
         $this->_stringHelper = $stringHelper;
         $this->_httpRequest = $request;
         $this->_scopeType = $scopeType;
+        $this->lifetimePath = $lifetimePath;
 
         /**
          * Session handler
@@ -170,8 +148,7 @@ class Config implements ConfigInterface
         /**
          * Cookie settings: lifetime, path, domain, httpOnly. These govern settings for the session cookie.
          */
-        $lifetime = $this->_scopeConfig->getValue($lifetimePath, $this->_scopeType);
-        $this->setCookieLifetime($lifetime, self::COOKIE_LIFETIME_DEFAULT);
+        $this->configureCookieLifetime();
 
         $path = $this->_scopeConfig->getValue(self::XML_PATH_COOKIE_PATH, $this->_scopeType);
         $path = empty($path) ? $this->_httpRequest->getBasePath() : $path;
@@ -580,4 +557,15 @@ class Config implements ConfigInterface
             throw new \BadMethodCallException(sprintf('Method "%s" does not exist in %s', $method, get_class($this)));
         }
     }
+
+    /**
+     * Set session cookie lifetime according to configuration
+     *
+     * @return $this
+     */
+    protected function configureCookieLifetime()
+    {
+        $lifetime = $this->_scopeConfig->getValue($this->lifetimePath, $this->_scopeType);
+        return $this->setCookieLifetime($lifetime, self::COOKIE_LIFETIME_DEFAULT);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
index 3c50426ce22347baf077bcf3f50739b5fa73bb6d..60d980f77df1ee84ddb89cbb22f116baa25a9322 100644
--- a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
+++ b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
@@ -376,7 +376,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
                 [
                     'session.save_handler' => 'files',
                     'session.cache_limiter' => 'files',
-                    'session.cookie_lifetime' => 7200,
+                    'session.cookie_lifetime' => 0,
                     'session.cookie_path' => '/',
                     'session.cookie_domain' => 'init.host',
                     'session.cookie_httponly' => false,
@@ -443,7 +443,6 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
 
         $this->configMock = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface');
         $getValueReturnMap = [
-            ['test_web/test_cookie/test_cookie_lifetime', 'store', null, 7200],
             ['web/cookie/cookie_path', 'store', null, ''],
         ];
         $this->configMock->method('getValue')
diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTimeFormatter.php b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTimeFormatter.php
index d4d136e0c9c14912e843b783d626c24487419e51..e0958f5635ad0c3cbfb6a63a19c106d143366a7e 100644
--- a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTimeFormatter.php
+++ b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTimeFormatter.php
@@ -18,21 +18,43 @@ class DateTimeFormatter implements DateTimeFormatterInterface
      */
     protected $useIntlFormatObject;
 
+    /**
+     * @var \Magento\Framework\Locale\ResolverInterface
+     */
+    private $localeResolver;
+
     /**
      * @param bool|null $useIntlFormatObject
      */
-    public function __construct($useIntlFormatObject = null)
-    {
+    public function __construct(
+        $useIntlFormatObject = null
+    ) {
         $this->useIntlFormatObject = (null === $useIntlFormatObject)
             ? !defined('HHVM_VERSION')
             : $useIntlFormatObject;
     }
 
+    /**
+     * Get locale resolver
+     *
+     * @return \Magento\Framework\Locale\ResolverInterface|mixed
+     */
+    private function getLocaleResolver()
+    {
+        if ($this->localeResolver === null) {
+            $this->localeResolver = \Magento\Framework\App\ObjectManager::getInstance()->get(
+                'Magento\Framework\Locale\ResolverInterface'
+            );
+        }
+        return $this->localeResolver;
+    }
+
     /**
      * {@inheritdoc}
      */
     public function formatObject($object, $format = null, $locale = null)
     {
+        $locale = (null === $locale) ? $this->getLocaleResolver()->getLocale() : $locale;
         if ($this->useIntlFormatObject) {
             return \IntlDateFormatter::formatObject($object, $format, $locale);
         }
diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeFormatterTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeFormatterTest.php
index 2c6358bc6ddc042a70a321ac017e271220861dd0..7a08498555ecace563771b3c432118e008486779 100644
--- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeFormatterTest.php
+++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeFormatterTest.php
@@ -10,33 +10,58 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 class DateTimeFormatterTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Framework\Stdlib\DateTime\DateTimeFormatter
+     * @var ObjectManager
      */
-    protected $dateTimeFormatter;
+    protected $objectManager;
+
+    /**
+     * @var \Magento\Framework\Locale\ResolverInterface | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $localeResolverMock;
 
     protected function setUp()
     {
         if (defined('HHVM_VERSION')) {
             $this->markTestSkipped('Skip this test for hhvm due to problem with \IntlDateFormatter::formatObject');
         }
+        $this->objectManager = new ObjectManager($this);
+        $this->localeResolverMock = $this->getMockBuilder('Magento\Framework\Locale\ResolverInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->localeResolverMock->expects($this->any())
+            ->method('getLocale')
+            ->willReturn('fr-FR');
 
-        $this->dateTimeFormatter = (new ObjectManager($this))
-            ->getObject('Magento\Framework\Stdlib\DateTime\DateTimeFormatter', [
-                'useIntlFormatObject' => false,
-            ]);
     }
 
     /**
      * @param \IntlCalendar|\DateTime $object
      * @param string|int|array|null $format
      * @param string|null $locale
+     * @param boolean $useIntlFormatObject
      * @dataProvider dataProviderFormatObject
      */
-    public function testFormatObject($object, $format = null, $locale = null)
+    public function testFormatObject($object, $format = null, $locale = null, $useIntlFormatObject = false)
     {
+        $dateTimeFormatter = $this->objectManager->getObject(
+            'Magento\Framework\Stdlib\DateTime\DateTimeFormatter',
+            [
+                'useIntlFormatObject' => $useIntlFormatObject,
+            ]
+        );
+
+        $reflection = new \ReflectionClass(get_class($dateTimeFormatter));
+        $reflectionProperty = $reflection->getProperty('localeResolver');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($dateTimeFormatter, $this->localeResolverMock);
+
         $this->assertEquals(
-            \IntlDateFormatter::formatObject($object, $format, $locale),
-            $this->dateTimeFormatter->formatObject($object, $format, $locale)
+            \IntlDateFormatter::formatObject(
+                $object,
+                $format,
+                (null === $locale) ? 'fr-FR' : $locale
+            ),
+            $dateTimeFormatter->formatObject($object, $format, $locale)
         );
     }
 
@@ -59,7 +84,6 @@ class DateTimeFormatterTest extends \PHPUnit_Framework_TestCase
             [new \DateTime('2013-09-09 09:09:09 Europe/Madrid'), \IntlDateFormatter::FULL, 'es_ES'],
             [new \DateTime('2013-09-09 09:09:09 -01:00'), null, null],
             [new \DateTime('2013-09-09 09:09:09 +01:00'), null, null],
-
             [$calendar, null, null],
             [$calendar, \IntlDateFormatter::FULL, null],
             [$calendar, null, 'en-US'],
@@ -70,6 +94,26 @@ class DateTimeFormatterTest extends \PHPUnit_Framework_TestCase
             [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 Europe/Madrid'), \IntlDateFormatter::FULL, 'es_ES'],
             [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 -01:00'), null, null],
             [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 +01:00'), null, null],
+            [$date, null, null, true],
+            [$date, \IntlDateFormatter::FULL, null, true],
+            [$date, null, 'en-US', true],
+            [$date, [\IntlDateFormatter::SHORT, \IntlDateFormatter::FULL], 'en-US', true],
+            [$date, 'E y-MM-d HH,mm,ss.SSS v', 'en-US', true],
+            [$date, [\IntlDateFormatter::NONE, \IntlDateFormatter::FULL], null, true],
+            [$date, "d 'of' MMMM y", 'en_US', true],
+            [new \DateTime('2013-09-09 09:09:09 Europe/Madrid'), \IntlDateFormatter::FULL, 'es_ES', true],
+            [new \DateTime('2013-09-09 09:09:09 -01:00'), null, null, true],
+            [new \DateTime('2013-09-09 09:09:09 +01:00'), null, null, true],
+            [$calendar, null, null, true],
+            [$calendar, \IntlDateFormatter::FULL, null, true],
+            [$calendar, null, 'en-US', true],
+            [$calendar, [\IntlDateFormatter::SHORT, \IntlDateFormatter::FULL], 'en-US', true],
+            [$calendar, 'E y-MM-d HH,mm,ss.SSS v', 'en-US', true],
+            [$calendar, [\IntlDateFormatter::NONE, \IntlDateFormatter::FULL], null, true],
+            [$calendar, "d 'of' MMMM y", 'en_US', true],
+            [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 Europe/Madrid'), \IntlDateFormatter::FULL, 'es_ES', true],
+            [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 -01:00'), null, null, true],
+            [\IntlCalendar::fromDateTime('2013-09-09 09:09:09 +01:00'), null, null, true],
         ];
     }
 
@@ -79,6 +123,17 @@ class DateTimeFormatterTest extends \PHPUnit_Framework_TestCase
      */
     public function testFormatObjectIfPassedWrongFormat()
     {
-        $this->dateTimeFormatter->formatObject(new \DateTime('2013-06-06 17:05:06 Europe/Dublin'), new \StdClass());
+        $dateTimeFormatter = $this->objectManager->getObject(
+            'Magento\Framework\Stdlib\DateTime\DateTimeFormatter',
+            [
+                'useIntlFormatObject' => false,
+            ]
+        );
+
+        $reflection = new \ReflectionClass(get_class($dateTimeFormatter));
+        $reflectionProperty = $reflection->getProperty('localeResolver');
+        $reflectionProperty->setAccessible(true);
+        $reflectionProperty->setValue($dateTimeFormatter, $this->localeResolverMock);
+        $dateTimeFormatter->formatObject(new \DateTime('2013-06-06 17:05:06 Europe/Dublin'), new \StdClass());
     }
 }
diff --git a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
index 99440a39863f34a7951de2a2f845f241bd6064ea..ffa9368c5454d3937c1c23ca85f987e63d2d78f8 100644
--- a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
+++ b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
@@ -59,7 +59,7 @@ class Proxy extends \Magento\Framework\Translate\Inline implements
      */
     public function __sleep()
     {
-        return ['_subject', '_isShared'];
+        return ['subject', 'isShared'];
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/UrlFactory.php b/lib/internal/Magento/Framework/UrlFactory.php
index f3e65d808ca58ceb6aba8bbc05f24daa62b878b5..65c7d55f1aac584d59863eb19a7f55a992c532fc 100644
--- a/lib/internal/Magento/Framework/UrlFactory.php
+++ b/lib/internal/Magento/Framework/UrlFactory.php
@@ -11,7 +11,7 @@ namespace Magento\Framework;
 class UrlFactory
 {
     /**
-     * @var \Magento\Framework\ObjectManagerInterface
+     * @var ObjectManagerInterface
      */
     protected $_objectManager = null;
 
@@ -21,10 +21,10 @@ class UrlFactory
     protected $_instanceName = null;
 
     /**
-     * @param \Magento\Framework\ObjectManagerInterface $objectManager
+     * @param ObjectManagerInterface $objectManager
      * @param string $instanceName
      */
-    public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = 'Magento\Framework\UrlInterface')
+    public function __construct(ObjectManagerInterface $objectManager, $instanceName = UrlInterface::class)
     {
         $this->_objectManager = $objectManager;
         $this->_instanceName = $instanceName;
@@ -34,7 +34,7 @@ class UrlFactory
      * Create Url instance with specified parameters
      *
      * @param array $data
-     * @return \Magento\Framework\UrlInterface
+     * @return UrlInterface
      */
     public function create(array $data = [])
     {
diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php
index ef5679b220c2460dbc1e94d7d09760af0436fa31..d35bae829050054446b009c249b65d72baa849d8 100644
--- a/lib/internal/Magento/Framework/Validator/Factory.php
+++ b/lib/internal/Magento/Framework/Validator/Factory.php
@@ -10,8 +10,13 @@
 
 namespace Magento\Framework\Validator;
 
+use Magento\Framework\Cache\FrontendInterface;
+
 class Factory
 {
+    /** cache key */
+    const CACHE_KEY = __CLASS__;
+
     /**
      * @var \Magento\Framework\ObjectManagerInterface
      */
@@ -29,18 +34,47 @@ class Factory
      */
     private $isDefaultTranslatorInitialized = false;
 
+    /**
+     * @var \Magento\Framework\Module\Dir\Reader
+     */
+    private $moduleReader;
+
+    /**
+     * @var FrontendInterface
+     */
+    private $cache;
+
     /**
      * Initialize dependencies
      *
      * @param \Magento\Framework\ObjectManagerInterface $objectManager
      * @param \Magento\Framework\Module\Dir\Reader $moduleReader
+     * @param FrontendInterface $cache
      */
     public function __construct(
         \Magento\Framework\ObjectManagerInterface $objectManager,
-        \Magento\Framework\Module\Dir\Reader $moduleReader
+        \Magento\Framework\Module\Dir\Reader $moduleReader,
+        FrontendInterface $cache
     ) {
         $this->_objectManager = $objectManager;
-        $this->_configFiles = $moduleReader->getConfigurationFiles('validation.xml');
+        $this->moduleReader = $moduleReader;
+        $this->cache = $cache;
+    }
+
+    /**
+     * Init cached list of validation files
+     */
+    protected function _initializeConfigList()
+    {
+        if (!$this->_configFiles) {
+            $this->_configFiles = $this->cache->load(self::CACHE_KEY);
+            if (!$this->_configFiles) {
+                $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml');
+                $this->cache->save(serialize($this->_configFiles), self::CACHE_KEY);
+            } else {
+                $this->_configFiles = unserialize($this->_configFiles);
+            }
+        }
     }
 
     /**
@@ -73,6 +107,7 @@ class Factory
      */
     public function getValidatorConfig()
     {
+        $this->_initializeConfigList();
         $this->_initializeDefaultTranslator();
         return $this->_objectManager->create('Magento\Framework\Validator\Config', ['configFiles' => $this->_configFiles]);
     }
diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
index 222510a23bfc3839b10bb225ea59aeb3a0ff950c..d685e332a56118490139cc668d0026583c572e5b 100644
--- a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
+++ b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
@@ -29,6 +29,8 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $_validatorConfig;
 
+    private $cache;
+
     /**
      * @var \Magento\Framework\Translate\AdapterInterface|null
      */
@@ -88,6 +90,9 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
         $this->_translateAdapter = $this->getMockBuilder(
             'Magento\Framework\TranslateInterface'
         )->disableOriginalConstructor()->getMock();
+
+        $this->cache = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class)
+            ->getMockForAbstractClass();
     }
 
     /**
@@ -107,7 +112,7 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
         $factory = new \Magento\Framework\Validator\Factory(
             $this->_objectManager,
             $this->_config,
-            $this->_translateAdapter
+            $this->cache
         );
         $actualConfig = $factory->getValidatorConfig();
         $this->assertInstanceOf(
@@ -147,7 +152,7 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
         $factory = new \Magento\Framework\Validator\Factory(
             $this->_objectManager,
             $this->_config,
-            $this->_translateAdapter
+            $this->cache
         );
         $this->assertInstanceOf(
             'Magento\Framework\Validator\Builder',
@@ -174,7 +179,7 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
         $factory = new \Magento\Framework\Validator\Factory(
             $this->_objectManager,
             $this->_config,
-            $this->_translateAdapter
+            $this->cache
         );
         $this->assertInstanceOf('Magento\Framework\Validator', $factory->createValidator('test', 'class', []));
     }
diff --git a/lib/internal/Magento/Framework/ValidatorFactory.php b/lib/internal/Magento/Framework/ValidatorFactory.php
index 6ec20b848735893f948d14562b2a66591b8dda30..6202a8b52cf3cbc201da18befa7f1e3fd0eccac8 100644
--- a/lib/internal/Magento/Framework/ValidatorFactory.php
+++ b/lib/internal/Magento/Framework/ValidatorFactory.php
@@ -10,7 +10,7 @@ namespace Magento\Framework;
  */
 class ValidatorFactory
 {
-    const DEFAULT_INSTANCE_NAME = 'Magento\Framework\Validator';
+    const DEFAULT_INSTANCE_NAME = Validator::class;
 
     /**
      * Object Manager instance
diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
index bfed571826a805cbeb263704644171dae4297cb6..2a48f91117cc15858fd8743b706438ae3ab174b2 100644
--- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
+++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Framework\View\Element;
 
+use Magento\Framework\DataObject\IdentityInterface;
+
 /**
  * Base Content Block class
  *
@@ -965,16 +967,17 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
         if ($this->hasData('cache_key')) {
             return static::CACHE_KEY_PREFIX . $this->getData('cache_key');
         }
+
         /**
          * don't prevent recalculation by saving generated cache key
          * because of ability to render single block instance with different data
          */
         $key = $this->getCacheKeyInfo();
-        //ksort($key);  // ignore order
-        $key = array_values($key);
-        // ignore array keys
+
+        $key = array_values($key);  // ignore array keys
+
         $key = implode('|', $key);
-        $key = sha1($key);
+        $key = sha1($key); // use hashing to hide potentially private data
         return static::CACHE_KEY_PREFIX . $key;
     }
 
@@ -991,6 +994,10 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
             $tags = $this->getData('cache_tags');
         }
         $tags[] = self::CACHE_GROUP;
+
+        if ($this instanceof IdentityInterface) {
+            $tags += $this->getIdentities();
+        }
         return $tags;
     }
 
@@ -1047,7 +1054,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
             $data
         );
 
-        $this->_cache->save($data, $cacheKey, $this->getCacheTags(), $this->getCacheLifetime());
+        $this->_cache->save($data, $cacheKey, array_unique($this->getCacheTags()), $this->getCacheLifetime());
         return $this;
     }
 
diff --git a/nginx.conf.sample b/nginx.conf.sample
index 87b28aa9f691d71666b2a91b433b77b1b4d672c2..04e6dda144b3cc6ea00f36a4004d234d6df20f7f 100644
--- a/nginx.conf.sample
+++ b/nginx.conf.sample
@@ -80,6 +80,12 @@ location /static/ {
     if ($MAGE_MODE = "production") {
         expires max;
     }
+    
+    # Remove signature of the static files that is used to overcome the browser cache
+    location ~ ^/static/version {
+        rewrite ^/static/(version\d*/)?(.*)$ /static/$2 last;
+    }
+    
     location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ {
         add_header Cache-Control "public";
         add_header X-Frame-Options "SAMEORIGIN";
@@ -145,9 +151,10 @@ location ~ cron\.php {
 location ~ (index|get|static|report|404|503)\.php$ {
     try_files $uri =404;
     fastcgi_pass   fastcgi_backend;
+    fastcgi_buffers 1024 4k;
 
     fastcgi_param  PHP_FLAG  "session.auto_start=off \n suhosin.session.cryptua=off";
-    fastcgi_param  PHP_VALUE "memory_limit=256M \n max_execution_time=600";
+    fastcgi_param  PHP_VALUE "memory_limit=768M \n max_execution_time=600";
     fastcgi_read_timeout 600s;
     fastcgi_connect_timeout 600s;
     fastcgi_param  MAGE_MODE $MAGE_MODE;
diff --git a/setup/src/Magento/Setup/Fixtures/StoresFixture.php b/setup/src/Magento/Setup/Fixtures/StoresFixture.php
index fd694bc35de2ce86667d2b487d631714ef170aa9..21b0da0d01a9acaa3e425ccb218d9b66fe54d5a2 100644
--- a/setup/src/Magento/Setup/Fixtures/StoresFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/StoresFixture.php
@@ -33,7 +33,6 @@ class StoresFixture extends Fixture
         /** @var \Magento\Store\Model\StoreManager $storeManager */
         $storeManager = $this->fixtureModel->getObjectManager()->create('Magento\Store\Model\StoreManager');
         /** @var $category \Magento\Catalog\Model\Category */
-        $category = $this->fixtureModel->getObjectManager()->create('Magento\Catalog\Model\Category');
 
         /** @var $defaultWebsite \Magento\Store\Model\Website */
         $defaultWebsite = $storeManager->getWebsite();
@@ -76,19 +75,18 @@ class StoresFixture extends Fixture
         //Create $storeGroupsCount websites
         $websiteNumber = 0;
         for ($i = 0; $i < $storeGroupsCount; $i++) {
+            $category = $this->fixtureModel->getObjectManager()->create('Magento\Catalog\Model\Category');
             $websiteId = $websitesId[$websiteNumber];
             $groupId = null;
-            $parentCategoryId = null;
             $categoryPath = '1';
 
             $storeGroupName = sprintf('Store Group %d - website_id_%d', $i + 1, $websiteId);
 
             if ($i == 0 && $websiteId == $defaultWebsiteId) {
                 $groupId = $defaultStoreGroupId;
-                $parentCategoryId = $defaultParentCategoryId;
                 $categoryPath = '1/' . $defaultParentCategoryId;
+                $category->load($defaultParentCategoryId);
             }
-            $category->load($parentCategoryId);
 
             $category->setName("Category $storeGroupName")
                 ->setPath($categoryPath)