diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Selection.php b/app/code/Magento/Bundle/Model/ResourceModel/Selection.php
index 5fbaea72417b03dd5195a6c81a130454203408d4..b67f9c0f3143fd19fb2752e9be4618b9bc622ba0 100644
--- a/app/code/Magento/Bundle/Model/ResourceModel/Selection.php
+++ b/app/code/Magento/Bundle/Model/ResourceModel/Selection.php
@@ -120,10 +120,11 @@ class Selection extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
             true
         )->from(
             $this->getMainTable(),
-            'parent_product_id'
+            ''
         )->join(
             ['e' => $this->metadataPool->getMetadata(ProductInterface::class)->getEntityTable()],
-            'e.' . $metadata->getLinkField() . ' = ' .  $this->getMainTable() . '.parent_product_id'
+            'e.' . $metadata->getLinkField() . ' = ' .  $this->getMainTable() . '.parent_product_id',
+            ['e.entity_id as parent_product_id']
         )->where(
             'e.entity_id IN(?)',
             $childId
diff --git a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php
index 42e751491860f8f20f54eb7026d5db5fb59d89d1..6bc4c9d5f212c93f1a0235d1ebf91ad505af677c 100644
--- a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php
+++ b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php
@@ -98,6 +98,21 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
      */
     protected $dataObjectHelperMock;
 
+    /**
+     * @var \Magento\Framework\Model\Entity\MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadataPoolMock;
+
+    /**
+     * @var \Magento\Framework\Model\Entity\EntityMetadata|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadataMock;
+
+    /**
+     * @var string
+     */
+    protected $linkField = 'product_id';
+
     protected function setUp()
     {
         $helper = new ObjectManager($this);
@@ -122,7 +137,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
         $this->product = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getTypeInstance', 'getStoreId', 'getTypeId', '__wakeup', 'getId'])
+            ->setMethods(['getTypeInstance', 'getStoreId', 'getTypeId', '__wakeup', 'getId', 'getData'])
             ->disableOriginalConstructor()
             ->getMock();
         $this->link = $this->getMockBuilder('\Magento\Bundle\Api\Data\LinkInterface')
@@ -142,6 +157,15 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
             '\Magento\Bundle\Model\ResourceModel\Option\CollectionFactory', ['create'], [], '', false
         );
         $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface', [], [], '', false);
+        $this->metadataPoolMock = $this->getMockBuilder('\Magento\Framework\Model\Entity\MetadataPool')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->metadataMock = $this->getMockBuilder('\Magento\Framework\Model\Entity\EntityMetadata')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->metadataPoolMock->expects($this->any())->method('getMetadata')
+            ->with(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->willReturn($this->metadataMock);
 
         $this->dataObjectHelperMock = $this->getMockBuilder('\Magento\Framework\Api\DataObjectHelper')
             ->disableOriginalConstructor()
@@ -156,6 +180,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
                 'optionCollection' => $this->optionCollectionFactoryMock,
                 'storeManager' => $this->storeManagerMock,
                 'dataObjectHelper' => $this->dataObjectHelperMock,
+                'metadataPool' => $this->metadataPoolMock
             ]
         );
     }
@@ -185,7 +210,8 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
             ->with($this->equalTo($this->selectionCollection))
             ->will($this->returnValue([$this->option]));
 
-        $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$this->product]));
+        $this->option->expects($this->any())->method('getSelections')->willReturn([$this->product]);
+        $this->product->expects($this->any())->method('getData')->willReturn([]);
 
         $this->dataObjectHelperMock->expects($this->once())
             ->method('populateWithArray')
@@ -316,11 +342,15 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku'));
         $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1));
 
+        $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField);
         $productMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue(
             \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
         ));
-        $productMock->expects($this->any())->method('getId')->will($this->returnValue('product_id'));
+        $productMock->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn($this->linkField);
 
         $linkedProductMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13));
@@ -340,7 +370,13 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $option->expects($this->once())->method('getId')->will($this->returnValue(1));
 
-        $optionsCollectionMock = $this->getMock('\Magento\Bundle\Model\ResourceModel\Option\Collection', [], [], '', false);
+        $optionsCollectionMock = $this->getMock(
+            '\Magento\Bundle\Model\ResourceModel\Option\Collection',
+            [],
+            [],
+            '',
+            false
+        );
         $optionsCollectionMock->expects($this->once())
             ->method('setIdFilter')
             ->with($this->equalTo('1'))
@@ -353,7 +389,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         );
 
         $bundle = $this->getMock('\Magento\Bundle\Model\ResourceModel\Bundle', [], [], '', false);
-        $bundle->expects($this->once())->method('getSelectionsData')->with('product_id')->will($this->returnValue([]));
+        $bundle->expects($this->once())->method('getSelectionsData')->with($this->linkField)->willReturn([]);
         $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle));
         $this->model->addChild($productMock, 1, $productLink);
     }
@@ -367,11 +403,15 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku'));
         $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1));
 
+        $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField);
         $productMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue(
             \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
         ));
-        $productMock->expects($this->any())->method('getId')->will($this->returnValue('product_id'));
+        $productMock->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn($this->linkField);
 
         $linkedProductMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $linkedProductMock->expects($this->any())->method('getEntityId')->will($this->returnValue(13));
@@ -391,7 +431,13 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $option->expects($this->once())->method('getId')->will($this->returnValue(1));
 
-        $optionsCollectionMock = $this->getMock('\Magento\Bundle\Model\ResourceModel\Option\Collection', [], [], '', false);
+        $optionsCollectionMock = $this->getMock(
+            '\Magento\Bundle\Model\ResourceModel\Option\Collection',
+            [],
+            [],
+            '',
+            false
+        );
         $optionsCollectionMock->expects($this->once())
             ->method('setIdFilter')
             ->with($this->equalTo(1))
@@ -409,7 +455,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         ];
         $bundle = $this->getMock('\Magento\Bundle\Model\ResourceModel\Bundle', [], [], '', false);
         $bundle->expects($this->once())->method('getSelectionsData')
-            ->with('product_id')
+            ->with($this->linkField)
             ->will($this->returnValue($selections));
         $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle));
         $this->model->addChild($productMock, 1, $productLink);
@@ -424,11 +470,16 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku'));
         $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1));
 
+        $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField);
         $productMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue(
             \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
         ));
-        $productMock->expects($this->any())->method('getId')->will($this->returnValue('product_id'));
+        $productMock->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn($this->linkField);
+
 
         $linkedProductMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13));
@@ -468,7 +519,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         ];
         $bundle = $this->getMock('\Magento\Bundle\Model\ResourceModel\Bundle', [], [], '', false);
         $bundle->expects($this->once())->method('getSelectionsData')
-            ->with('product_id')
+            ->with($this->linkField)
             ->will($this->returnValue($selections));
         $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle));
 
@@ -491,11 +542,15 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku'));
         $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1));
 
+        $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField);
         $productMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue(
             \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
         ));
-        $productMock->expects($this->any())->method('getId')->will($this->returnValue('product_id'));
+        $productMock->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn($this->linkField);
 
         $linkedProductMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13));
@@ -535,7 +590,7 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         ];
         $bundle = $this->getMock('\Magento\Bundle\Model\ResourceModel\Bundle', [], [], '', false);
         $bundle->expects($this->once())->method('getSelectionsData')
-            ->with('product_id')
+            ->with($this->linkField)
             ->will($this->returnValue($selections));
         $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle));
 
@@ -572,11 +627,15 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $productLink->expects($this->any())->method('getCanChangeQuantity')->will($this->returnValue($canChangeQuantity));
         $productLink->expects($this->any())->method('getIsDefault')->will($this->returnValue($isDefault));
 
+        $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField);
         $productMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue(
             \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
         ));
-        $productMock->expects($this->any())->method('getEntityId')->will($this->returnValue($parentProductId));
+        $productMock->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn($parentProductId);
 
         $linkedProductMock = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
         $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue($linkProductId));
@@ -866,7 +925,11 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase
         $selection->expects($this->any())->method('getProductId')->willReturn($productId);
 
         $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection]));
-        $this->product->expects($this->any())->method('getId')->will($this->returnValue(3));
+        $this->metadataMock->expects($this->any())->method('getLinkField')->willReturn($this->linkField);
+        $this->product->expects($this->any())
+            ->method('getData')
+            ->with($this->linkField)
+            ->willReturn(3);
 
         $bundle->expects($this->once())->method('dropAllUnneededSelections')->with(3, []);
         $bundle->expects($this->once())->method('removeProductRelations')->with(3, [$productId]);
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
index 828cc31c1725cec790e89707948859e69c15d25a..e64d8c2ef84fa9c3d777eacbaaebfebf4ee00e60 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
@@ -359,13 +359,8 @@ class HelperTest extends \PHPUnit_Framework_TestCase
     {
         $linkMock = $this->getMockBuilder(ProductLinkInterface::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getLinkType'])
             ->getMockForAbstractClass();
 
-        $linkMock->expects($this->once())
-            ->method('getLinkType')
-            ->willReturn('upsell');
-
         $this->productMock->expects($this->any())
             ->method('getProductLinks')
             ->willReturn([$linkMock]);
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml
index 25aaaea0ffe2b98a6e46a7e133c53b743603530e..284f241b3ba54f834f2d5057bf1a67458645aa03 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml
@@ -10,7 +10,7 @@
 
 <?php $_divId = 'tree-div_' . time() ?>
 <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div>
-<script id="ie-deferred-loader" defer="defer" src=""></script>
+<script id="ie-deferred-loader" defer="defer" src="//:"></script>
 <script>
     require([
         'jquery',
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
index 9e303fe921dfd328453c0f736a107fa4be70e6fa..ae65d2658978a3f2e74dc04278ce9d803dda471a 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml
@@ -11,7 +11,7 @@
 <?php $_divId = 'tree' . $block->getId() ?>
 <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div>
 <!--[if IE]>
-<script id="ie-deferred-loader" defer="defer" src=""></script>
+<script id="ie-deferred-loader" defer="defer" src="//:"></script>
 <![endif]-->
 <script>
 require(['jquery', "prototype", "extjs/ext-tree-checkbox"], function(jQuery){
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
index f1bba6cc3c07ca0603e149afc8f0ad24b342a467..b3a7002ef417ecb3cc14006aa34b6844a32d8a67 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
@@ -38,7 +38,7 @@
             <span class="title"><?php /* @escapeNotVerified */ echo __('Unassigned Attributes') ?></span>
         </div>
         <div id="tree-div2" class="attribute-set-tree"></div>
-        <script id="ie-deferred-loader" defer="defer" src=""></script>
+        <script id="ie-deferred-loader" defer="defer" src="//:"></script>
         <script>
             define("tree-panel",
                 [
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index be5131c2b3bdceb099cb84455ac2b1c7562c0752..415ec8bd0072491aef33fecc72f85cd2640b221e 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -1162,7 +1162,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
             foreach ($skuData as $sku => $attributes) {
                 $linkId = $this->_connection->fetchOne(
                     $this->_connection->select()
-                        ->from($this->_resource->getTable('catalog_product_entity'))
+                        ->from($this->getResource()->getTable('catalog_product_entity'))
                         ->where('sku = ?', $sku)
                         ->columns($metadata->getLinkField())
                 );
@@ -1971,7 +1971,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
                 $productIdsToReindex[] = $row['product_id'];
 
                 $row['website_id'] = $this->stockConfiguration->getDefaultScopeId();
-                $row['stock_id'] = $this->stockRegistry->getStock()->getStockId();
+                $row['stock_id'] = $this->stockRegistry->getStock($row['website_id'])->getStockId();
 
                 $stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']);
                 $existStockData = $stockItemDo->getData();
diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
index fd2d6e9fb4646e67cb7e688253fed3178812d417..ae443adc9b5872ed9d41704cdc08c807ab5f3e8f 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
@@ -527,10 +527,28 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
                 ]
             ]
         ];
-        $this->skuProcessor->expects($this->once())
-            ->method('getNewSku')
-            ->with($testSku)
-            ->willReturn(['entity_id' => self::ENTITY_ID]);
+        $metadataMock = $this->getMockBuilder('\Magento\Framework\Model\Entity\EntityMetadata')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->metadataPool->expects($this->any())->method('getMetadata')
+            ->with(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->willReturn($metadataMock);
+        $metadataMock->expects($this->any())->method('getLinkField')->willReturn('entity_id');
+        $entityTable = 'catalog_product_entity';
+        $resource = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModel')
+            ->disableOriginalConstructor()
+            ->setMethods(['getTable'])
+            ->getMock();
+        $resource->expects($this->once())->method('getTable')->with($entityTable)->willReturnArgument(0);
+        $this->_resourceFactory->expects($this->once())->method('create')->willReturn($resource);
+        $selectMock = $this->getMockBuilder('Magento\Framework\DB\Select')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $selectMock->expects($this->once())->method('from')->with($entityTable, '*', null)->willReturnSelf();
+        $selectMock->expects($this->once())->method('where')->with('sku = ?', $testSku)->willReturnSelf();
+        $selectMock->expects($this->once())->method('columns')->with('entity_id')->willReturnSelf();
+        $this->_connection->expects($this->any())->method('fetchOne')->willReturn(self::ENTITY_ID);
+        $this->_connection->expects($this->any())->method('select')->willReturn($selectMock);
         $this->_connection->expects($this->any())
             ->method('quoteInto')
             ->willReturnCallback([$this, 'returnQuoteCallback']);
diff --git a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
index 27b45dc6771632f08a2f8aa62619d6324bcf7d02..2566aff49b0dc9cea7412906f8096ea9df7d48f6 100644
--- a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
@@ -12,10 +12,10 @@ namespace Magento\CatalogInventory\Api;
 interface StockRegistryInterface
 {
     /**
-     * @param int|null $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockInterface
      */
-    public function getStock($stockId = null);
+    public function getStock($scopeId = null);
 
     /**
      * @param int $productId
diff --git a/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php b/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
index 44d3c1be11fab1f18b79bb0cf269198688c54eee..bb3d101e97f9f830c81941e0cfc0670e79ad6f49 100644
--- a/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
@@ -15,7 +15,7 @@ interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaIn
      * Add Criteria object
      *
      * @param \Magento\CatalogInventory\Api\StockStatusCriteriaInterface $criteria
-     * @return void
+     * @return bool
      */
     public function addCriteria(\Magento\CatalogInventory\Api\StockStatusCriteriaInterface $criteria);
 
@@ -23,7 +23,7 @@ interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaIn
      * Filter by scope(s)
      *
      * @param int $scope
-     * @return void
+     * @return bool
      */
     public function setScopeFilter($scope);
 
@@ -31,7 +31,7 @@ interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaIn
      * Add product(s) filter
      *
      * @param int $products
-     * @return void
+     * @return bool
      */
     public function setProductsFilter($products);
 
@@ -39,7 +39,7 @@ interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaIn
      * Add filter by quantity
      *
      * @param float $qty
-     * @return void
+     * @return bool
      */
     public function setQtyFilter($qty);
 }
diff --git a/app/code/Magento/CatalogInventory/Helper/Stock.php b/app/code/Magento/CatalogInventory/Helper/Stock.php
index 4706a60bf5250a18f4ebcfb7dd10efc9cf3fe8ed..8a227f53ebc1fce93a93a809ab5a0c1e887c06c4 100644
--- a/app/code/Magento/CatalogInventory/Helper/Stock.php
+++ b/app/code/Magento/CatalogInventory/Helper/Stock.php
@@ -6,7 +6,6 @@
 namespace Magento\CatalogInventory\Helper;
 
 use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 use Magento\Store\Model\StoreManagerInterface;
 use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\CatalogInventory\Model\ResourceModel\Stock\StatusFactory;
@@ -48,45 +47,36 @@ class Stock
      */
     private $stockRegistryProvider;
 
-    /**
-     * @var StockResolverInterface
-     */
-    protected $stockResolver;
-
     /**
      * @param StoreManagerInterface $storeManager
      * @param ScopeConfigInterface $scopeConfig
      * @param StatusFactory $stockStatusFactory
      * @param StockRegistryProviderInterface $stockRegistryProvider
-     * @param StockResolverInterface $stockResolver
      */
     public function __construct(
         StoreManagerInterface $storeManager,
         ScopeConfigInterface $scopeConfig,
         StatusFactory $stockStatusFactory,
-        StockRegistryProviderInterface $stockRegistryProvider,
-        StockResolverInterface $stockResolver
+        StockRegistryProviderInterface $stockRegistryProvider
     ) {
         $this->storeManager = $storeManager;
         $this->scopeConfig = $scopeConfig;
         $this->stockStatusFactory  = $stockStatusFactory;
         $this->stockRegistryProvider = $stockRegistryProvider;
-        $this->stockResolver = $stockResolver;
     }
 
     /**
      * Assign stock status information to product
      *
      * @param Product $product
-     * @param int $stockStatus
+     * @param int $status
      * @return void
      */
-    public function assignStatusToProduct(Product $product, $stockStatus = null)
+    public function assignStatusToProduct(Product $product, $status = null)
     {
-        if ($stockStatus === null) {
-            $productId = $product->getId();
-            $stockId = $this->stockResolver->getStockId($productId, $product->getStore()->getWebsiteId());
-            $stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $stockId);
+        if ($status === null) {
+            $websiteId = $product->getStore()->getWebsiteId();
+            $stockStatus = $this->stockRegistryProvider->getStockStatus($product->getId(), $websiteId);
             $status = $stockStatus->getStockStatus();
         }
         $product->setIsSalable($status);
@@ -103,8 +93,7 @@ class Stock
         $websiteId = $this->storeManager->getStore($productCollection->getStoreId())->getWebsiteId();
         foreach ($productCollection as $product) {
             $productId = $product->getId();
-            $stockId = $this->stockResolver->getStockId($productId, $websiteId);
-            $stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $stockId);
+            $stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $websiteId);
             $status = $stockStatus->getStockStatus();
             $product->setIsSalable($status);
         }
diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
index a08aa33533301bd12c4c285da368de9a9a4492ec..8fb68a2c997be868f117803c18c292f74f2e4076 100644
--- a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
+++ b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
@@ -11,22 +11,22 @@ namespace Magento\CatalogInventory\Model\Spi;
 interface StockRegistryProviderInterface
 {
     /**
-     * @param int|null $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockInterface
      */
-    public function getStock($stockId);
+    public function getStock($scopeId);
 
     /**
      * @param int $productId
-     * @param int $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockItemInterface
      */
-    public function getStockItem($productId, $stockId);
+    public function getStockItem($productId, $scopeId);
 
     /**
      * @param int $productId
-     * @param int $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockStatusInterface
      */
-    public function getStockStatus($productId, $stockId);
+    public function getStockStatus($productId, $scopeId);
 }
diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockResolverInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockResolverInterface.php
deleted file mode 100644
index b4f9df8f079da74b7d5ad09a0ccdd675fbcc8978..0000000000000000000000000000000000000000
--- a/app/code/Magento/CatalogInventory/Model/Spi/StockResolverInterface.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\CatalogInventory\Model\Spi;
-
-/**
- * Interface StockResolverInterface
- */
-interface StockResolverInterface
-{
-    /**
-     * @param int $productId
-     * @param int $websiteId
-     * @return int
-     */
-    public function getStockId($productId, $websiteId);
-}
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/Stock/Item.php
index b97977fd3c013e006c53232b490aedd7f7f857cc..3c4c7bfdf143f7ff892af8d03207e07c8dd90f06 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/Item.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/Item.php
@@ -177,7 +177,7 @@ class Item extends AbstractExtensibleModel implements StockItemInterface
     {
         $stockId = $this->getData(static::STOCK_ID);
         if ($stockId === null) {
-            $stockId = $this->stockRegistry->getStock()->getStockId();
+            $stockId = $this->stockRegistry->getStock($this->getWebsiteId())->getStockId();
         }
         return (int) $stockId;
     }
diff --git a/app/code/Magento/CatalogInventory/Model/StockIndex.php b/app/code/Magento/CatalogInventory/Model/StockIndex.php
index d961f52ff519f1eaf604207deb5e6bc287396565..4c6f6521c97949980222f9ff310f418a13ba247f 100644
--- a/app/code/Magento/CatalogInventory/Model/StockIndex.php
+++ b/app/code/Magento/CatalogInventory/Model/StockIndex.php
@@ -13,7 +13,6 @@ use Magento\Catalog\Model\Product\Website as ProductWebsite;
 use Magento\Catalog\Model\ProductFactory;
 use Magento\CatalogInventory\Api\StockIndexInterface;
 use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 use Magento\Catalog\Api\ProductRepositoryInterface;
 
 /**
@@ -56,31 +55,22 @@ class StockIndex implements StockIndexInterface
      */
     protected $productTypes = [];
 
-
-    /**
-     * @var StockResolverInterface
-     */
-    protected $stockResolver;
-
     /**
      * @param StockRegistryProviderInterface $stockRegistryProvider
      * @param ProductRepositoryInterface $productRepository
      * @param ProductWebsite $productWebsite
      * @param ProductType $productType
-     * @param StockResolverInterface $stockResolver
      */
     public function __construct(
         StockRegistryProviderInterface $stockRegistryProvider,
         ProductRepositoryInterface $productRepository,
         ProductWebsite $productWebsite,
-        ProductType $productType,
-        StockResolverInterface $stockResolver
+        ProductType $productType
     ) {
         $this->stockRegistryProvider = $stockRegistryProvider;
         $this->productRepository = $productRepository;
         $this->productWebsite = $productWebsite;
         $this->productType = $productType;
-        $this->stockResolver = $stockResolver;
     }
 
     /**
@@ -122,8 +112,7 @@ class StockIndex implements StockIndexInterface
      */
     public function updateProductStockStatus($productId, $websiteId)
     {
-        $stockId = $this->stockResolver->getStockId($productId, $websiteId);
-        $item = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $item = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
 
         $status = \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK;
         $qty = 0;
@@ -242,8 +231,7 @@ class StockIndex implements StockIndexInterface
         }
 
         foreach ($parentIds as $parentId) {
-            $stockId = $this->stockResolver->getStockId($productId, $websiteId);
-            $item = $this->stockRegistryProvider->getStockItem($parentId, $stockId);
+            $item = $this->stockRegistryProvider->getStockItem($parentId, $websiteId);
             $status = \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK;
             $qty = 0;
             if ($item->getItemId()) {
diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php
index 7b08d2a757ebcdd98eed33dd94e87d0b1496eb97..9415c464c18c1e4b9a0bb0a41d3e1cc587b0bfed 100644
--- a/app/code/Magento/CatalogInventory/Model/StockManagement.php
+++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php
@@ -10,7 +10,6 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\CatalogInventory\Api\StockManagementInterface;
 use Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface;
 use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\CatalogInventory\Model\ResourceModel\Stock as ResourceStock;
 
@@ -49,11 +48,6 @@ class StockManagement implements StockManagementInterface
      */
     private $qtyCounter;
 
-    /**
-     * @var StockResolverInterface
-     */
-    protected $stockResolver;
-
     /**
      * @param ResourceStock $stockResource
      * @param StockRegistryProviderInterface $stockRegistryProvider
@@ -61,7 +55,6 @@ class StockManagement implements StockManagementInterface
      * @param StockConfigurationInterface $stockConfiguration
      * @param ProductRepositoryInterface $productRepository
      * @param QtyCounterInterface $qtyCounter
-     * @param StockResolverInterface $stockResolver
      */
     public function __construct(
         ResourceStock $stockResource,
@@ -69,8 +62,7 @@ class StockManagement implements StockManagementInterface
         StockState $stockState,
         StockConfigurationInterface $stockConfiguration,
         ProductRepositoryInterface $productRepository,
-        QtyCounterInterface $qtyCounter,
-        StockResolverInterface $stockResolver
+        QtyCounterInterface $qtyCounter
     ) {
         $this->stockRegistryProvider = $stockRegistryProvider;
         $this->stockState = $stockState;
@@ -78,7 +70,6 @@ class StockManagement implements StockManagementInterface
         $this->productRepository = $productRepository;
         $this->qtyCounter = $qtyCounter;
         $this->resource = $stockResource;
-        $this->stockResolver = $stockResolver;
     }
 
     /**
@@ -93,9 +84,9 @@ class StockManagement implements StockManagementInterface
      */
     public function registerProductsSale($items, $websiteId = null)
     {
-        if (!$websiteId) {
-                $websiteId = $this->stockConfiguration->getDefaultScopeId();
-        }
+        //if (!$websiteId) {
+            $websiteId = $this->stockConfiguration->getDefaultScopeId();
+        //}
         $this->getResource()->beginTransaction();
         $lockedItems = $this->getResource()->lockProductsStock(array_keys($items), $websiteId);
         $fullSaveItems = $registeredItems = [];
@@ -103,8 +94,7 @@ class StockManagement implements StockManagementInterface
             $productId = $lockedItemRecord['product_id'];
             /** @var StockItemInterface $stockItem */
             $orderedQty = $items[$productId];
-            $stockId = $this->stockResolver->getStockId($productId, $websiteId);
-            $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+            $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
             $canSubtractQty = $stockItem->getItemId() && $this->canSubtractQty($stockItem);
             if (!$canSubtractQty || !$this->stockConfiguration->isQty($lockedItemRecord['type_id'])) {
                 continue;
@@ -142,9 +132,9 @@ class StockManagement implements StockManagementInterface
      */
     public function revertProductsSale($items, $websiteId = null)
     {
-        if (!$websiteId) {
-            $websiteId = $this->stockConfiguration->getDefaultScopeId();
-        }
+        //if (!$websiteId) {
+        $websiteId = $this->stockConfiguration->getDefaultScopeId();
+        //}
         $this->qtyCounter->correctItemsQty($items, $websiteId, '+');
         return true;
     }
@@ -159,11 +149,10 @@ class StockManagement implements StockManagementInterface
      */
     public function backItemQty($productId, $qty, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         if ($stockItem->getItemId() && $this->stockConfiguration->isQty($this->getProductType($productId))) {
             if ($this->canSubtractQty($stockItem)) {
                 $stockItem->setQty($stockItem->getQty() + $qty);
diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php
index d72719fdb3a0501e274a9d9d5b3990c072e5e3c0..fda30523c39ab445b8588ed5f65d0ea40e2e4e8f 100644
--- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php
+++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php
@@ -11,9 +11,7 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
 use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
 use Magento\CatalogInventory\Api\StockRegistryInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
-use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
 
 /**
  * Class StockRegistry
@@ -47,49 +45,37 @@ class StockRegistry implements StockRegistryInterface
      */
     protected $criteriaFactory;
 
-    /**
-     * @var \Magento\CatalogInventory\Model\Spi\StockResolverInterface
-     */
-    protected $stockResolver;
-
-    /**
-     * @var StockStateProviderInterface
-     */
-    protected $stockStateProvider;
-
     /**
      * @param StockConfigurationInterface $stockConfiguration
      * @param StockRegistryProviderInterface $stockRegistryProvider
      * @param StockItemRepositoryInterface $stockItemRepository
      * @param StockItemCriteriaInterfaceFactory $criteriaFactory
      * @param ProductFactory $productFactory
-     * @param StockResolverInterface $stockResolver
-     * @param StockStateProviderInterface $stockStateProvider
      */
     public function __construct(
         StockConfigurationInterface $stockConfiguration,
         StockRegistryProviderInterface $stockRegistryProvider,
         StockItemRepositoryInterface $stockItemRepository,
         StockItemCriteriaInterfaceFactory $criteriaFactory,
-        ProductFactory $productFactory,
-        StockResolverInterface $stockResolver,
-        StockStateProviderInterface $stockStateProvider
+        ProductFactory $productFactory
     ) {
         $this->stockConfiguration = $stockConfiguration;
         $this->stockRegistryProvider = $stockRegistryProvider;
         $this->stockItemRepository = $stockItemRepository;
         $this->criteriaFactory = $criteriaFactory;
         $this->productFactory = $productFactory;
-        $this->stockResolver = $stockResolver;
-        $this->stockStateProvider = $stockStateProvider;
     }
 
     /**
-     * @inheritdoc
+     * @param int $scopeId
+     * @return \Magento\CatalogInventory\Api\Data\StockInterface
      */
-    public function getStock($stockId = null)
+    public function getStock($scopeId = null)
     {
-        return $this->stockRegistryProvider->getStock($stockId);
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
+        return $this->stockRegistryProvider->getStock($scopeId);
     }
 
     /**
@@ -99,12 +85,10 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getStockItem($productId, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-
-        return $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
+        return $this->stockRegistryProvider->getStockItem($productId, $scopeId);
     }
 
     /**
@@ -115,12 +99,11 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getStockItemBySku($productSku, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
         $productId = $this->resolveProductId($productSku);
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        return $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        return $this->stockRegistryProvider->getStockItem($productId, $scopeId);
     }
 
     /**
@@ -130,11 +113,10 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getStockStatus($productId, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        return $this->stockRegistryProvider->getStockStatus($productId, $stockId);
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
+        return $this->stockRegistryProvider->getStockStatus($productId, $scopeId);
     }
 
     /**
@@ -145,12 +127,11 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getStockStatusBySku($productSku, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
         $productId = $this->resolveProductId($productSku);
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        return $this->getStockStatus($productId, $stockId);
+        return $this->getStockStatus($productId, $scopeId);
     }
 
     /**
@@ -161,11 +142,10 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getProductStockStatus($productId, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockStatus = $this->getStockStatus($productId, $stockId);
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
+        $stockStatus = $this->getStockStatus($productId, $scopeId);
         return $stockStatus->getStockStatus();
     }
 
@@ -177,12 +157,11 @@ class StockRegistry implements StockRegistryInterface
      */
     public function getProductStockStatusBySku($productSku, $scopeId = null)
     {
-        if (!$scopeId) {
-            $scopeId = $this->stockConfiguration->getDefaultScopeId();
-        }
+        //if (!$scopeId) {
+        $scopeId = $this->stockConfiguration->getDefaultScopeId();
+        //}
         $productId = $this->resolveProductId($productSku);
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        return $this->getProductStockStatus($productId, $stockId);
+        return $this->getProductStockStatus($productId, $scopeId);
     }
 
     /**
diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php b/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
index 0e103e2cddd14ff07bccc59a2239ca5d40a1a787..e8f7ae5cc723fb8219aaf9990c1d23017cb35b4e 100644
--- a/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
+++ b/app/code/Magento/CatalogInventory/Model/StockRegistryProvider.php
@@ -15,13 +15,11 @@ use Magento\CatalogInventory\Api\Data\StockStatusInterfaceFactory;
 use Magento\CatalogInventory\Api\StockCriteriaInterfaceFactory;
 use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
 use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory;
-use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\Store\Model\StoreManagerInterface;
 
 /**
  * Class StockRegistryProvider
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  */
 class StockRegistryProvider implements StockRegistryProviderInterface
 {
@@ -70,11 +68,6 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      */
     protected $stockStatusCriteriaFactory;
 
-    /**
-     * @var StockConfigurationInterface
-     */
-    protected $stockConfiguration;
-
     /**
      * @var array
      */
@@ -100,7 +93,6 @@ class StockRegistryProvider implements StockRegistryProviderInterface
      * @param StockCriteriaInterfaceFactory $stockCriteriaFactory
      * @param StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory
      * @param StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
-     * @param StockConfigurationInterface $stockConfiguration
      */
     public function __construct(
         StockRepositoryInterface $stockRepository,
@@ -111,8 +103,7 @@ class StockRegistryProvider implements StockRegistryProviderInterface
         StockStatusInterfaceFactory $stockStatusFactory,
         StockCriteriaInterfaceFactory $stockCriteriaFactory,
         StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory,
-        StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory,
-        StockConfigurationInterface $stockConfiguration
+        StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
     ) {
         $this->stockRepository = $stockRepository;
         $this->stockFactory = $stockFactory;
@@ -120,49 +111,43 @@ class StockRegistryProvider implements StockRegistryProviderInterface
         $this->stockItemFactory = $stockItemFactory;
         $this->stockStatusRepository = $stockStatusRepository;
         $this->stockStatusFactory = $stockStatusFactory;
+
         $this->stockCriteriaFactory = $stockCriteriaFactory;
         $this->stockItemCriteriaFactory = $stockItemCriteriaFactory;
         $this->stockStatusCriteriaFactory = $stockStatusCriteriaFactory;
-        $this->stockConfiguration = $stockConfiguration;
     }
 
     /**
-     * @inheritdoc
+     * @param int|null $scopeId
+     * @return \Magento\CatalogInventory\Api\Data\StockInterface
      */
-    public function getStock($stockId)
+    public function getStock($scopeId)
     {
-        if (!isset($this->stocks[$stockId])) {
-            if ($stockId !== null) {
-                $stock = $this->stockRepository->get($stockId);
-            } else {
-                /** @var \Magento\CatalogInventory\Api\StockCriteriaInterface $criteria */
-                $criteria = $this->stockCriteriaFactory->create();
-                $criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId());
-                $collection = $this->stockRepository->getList($criteria);
-                $stock = current($collection->getItems());
-            }
+        if (!isset($this->stocks[$scopeId])) {
+            $criteria = $this->stockCriteriaFactory->create();
+            $criteria->setScopeFilter($scopeId);
+            $collection = $this->stockRepository->getList($criteria);
+            $stock = current($collection->getItems());
             if ($stock && $stock->getStockId()) {
-                $this->stocks[$stockId] = $stock;
+                $this->stocks[$scopeId] = $stock;
             } else {
                 return $this->stockFactory->create();
             }
         }
-        return $this->stocks[$stockId];
+        return $this->stocks[$scopeId];
     }
 
     /**
      * @param int $productId
-     * @param int $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockItemInterface
      */
-    public function getStockItem($productId, $stockId)
+    public function getStockItem($productId, $scopeId)
     {
-        $key = $stockId . '/' . $productId;
+        $key = $scopeId . '/' . $productId;
         if (!isset($this->stockItems[$key])) {
-            /** @var \Magento\CatalogInventory\Api\StockItemCriteriaInterface $criteria */
             $criteria = $this->stockItemCriteriaFactory->create();
             $criteria->setProductsFilter($productId);
-            $criteria->setStockFilter($this->getStock($stockId));
             $collection = $this->stockItemRepository->getList($criteria);
             $stockItem = current($collection->getItems());
             if ($stockItem && $stockItem->getItemId()) {
@@ -176,17 +161,16 @@ class StockRegistryProvider implements StockRegistryProviderInterface
 
     /**
      * @param int $productId
-     * @param int $stockId
+     * @param int $scopeId
      * @return \Magento\CatalogInventory\Api\Data\StockStatusInterface
      */
-    public function getStockStatus($productId, $stockId)
+    public function getStockStatus($productId, $scopeId)
     {
-        $key = $stockId . '/' . $productId;
+        $key = $scopeId . '/' . $productId;
         if (!isset($this->stockStatuses[$key])) {
-            /** @var \Magento\CatalogInventory\Api\stockStatusCriteriaInterface $criteria */
             $criteria = $this->stockStatusCriteriaFactory->create();
             $criteria->setProductsFilter($productId);
-            $criteria->addFilter('stock', 'stock_id', $stockId);
+            $criteria->setScopeFilter($scopeId);
             $collection = $this->stockStatusRepository->getList($criteria);
             $stockStatus = current($collection->getItems());
             if ($stockStatus && $stockStatus->getProductId()) {
diff --git a/app/code/Magento/CatalogInventory/Model/StockResolver.php b/app/code/Magento/CatalogInventory/Model/StockResolver.php
deleted file mode 100644
index 8e7c096d37047e7ae2c4cc40da87b6cbcb3f2c16..0000000000000000000000000000000000000000
--- a/app/code/Magento/CatalogInventory/Model/StockResolver.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\CatalogInventory\Model;
-
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
-
-/**
- * Class StockResolver
- */
-class StockResolver implements StockResolverInterface
-{
-    const DEFAULT_STOCK_ID = 1;
-
-    /**
-     * @inheritdoc
-     */
-    public function getStockId($productId, $websiteId)
-    {
-        $stockId = self::DEFAULT_STOCK_ID;
-        return $stockId;
-    }
-}
diff --git a/app/code/Magento/CatalogInventory/Model/StockState.php b/app/code/Magento/CatalogInventory/Model/StockState.php
index e64906691ad79ca2e8c65ddfe7738711b628101a..c3d0dc9aef0fd317d0d69e8b1b06b01b5af4f7e7 100644
--- a/app/code/Magento/CatalogInventory/Model/StockState.php
+++ b/app/code/Magento/CatalogInventory/Model/StockState.php
@@ -9,7 +9,6 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\CatalogInventory\Api\StockStateInterface;
 use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
 use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 
 /**
  * Interface StockState
@@ -31,27 +30,19 @@ class StockState implements StockStateInterface
      */
     protected $stockConfiguration;
 
-    /**
-     * @var StockResolverInterface
-     */
-    protected $stockResolver;
-
     /**
      * @param StockStateProviderInterface $stockStateProvider
      * @param StockRegistryProviderInterface $stockRegistryProvider
      * @param StockConfigurationInterface $stockConfiguration
-     * @param StockResolverInterface $stockResolver
      */
     public function __construct(
         StockStateProviderInterface $stockStateProvider,
         StockRegistryProviderInterface $stockRegistryProvider,
-        StockConfigurationInterface $stockConfiguration,
-        StockResolverInterface $stockResolver
+        StockConfigurationInterface $stockConfiguration
     ) {
         $this->stockStateProvider = $stockStateProvider;
         $this->stockRegistryProvider = $stockRegistryProvider;
         $this->stockConfiguration = $stockConfiguration;
-        $this->stockResolver = $stockResolver;
     }
 
     /**
@@ -64,8 +55,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->verifyStock($stockItem);
     }
 
@@ -79,8 +69,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->verifyNotification($stockItem);
     }
 
@@ -98,8 +87,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->checkQty($stockItem, $qty);
     }
 
@@ -117,8 +105,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->suggestQty($stockItem, $qty);
     }
 
@@ -134,8 +121,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->getStockQty($stockItem);
     }
 
@@ -150,8 +136,7 @@ class StockState implements StockStateInterface
         if ($websiteId === null) {
             $websiteId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $websiteId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
         return $this->stockStateProvider->checkQtyIncrements($stockItem, $qty);
     }
 
@@ -168,8 +153,7 @@ class StockState implements StockStateInterface
         if ($scopeId === null) {
             $scopeId = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockId = $this->stockResolver->getStockId($productId, $scopeId);
-        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $stockId);
+        $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
         return $this->stockStateProvider->checkQuoteItemQty($stockItem, $itemQty, $qtyToCheck, $origQty);
     }
 }
diff --git a/app/code/Magento/CatalogInventory/Observer/AddInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/AddInventoryDataObserver.php
index 402807a624217f3989c2adc6f6371c1f3b05cb68..2e9ce4a393fab4de8e9ce06f8cc163aea9a10658 100644
--- a/app/code/Magento/CatalogInventory/Observer/AddInventoryDataObserver.php
+++ b/app/code/Magento/CatalogInventory/Observer/AddInventoryDataObserver.php
@@ -34,10 +34,7 @@ class AddInventoryDataObserver implements ObserverInterface
     {
         $product = $observer->getEvent()->getProduct();
         if ($product instanceof \Magento\Catalog\Model\Product) {
-            $this->stockHelper->assignStatusToProduct(
-                $product,
-                $product->getStockStatus()
-            );
+            $this->stockHelper->assignStatusToProduct($product);
         }
     }
 }
diff --git a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php
index 84c6e31dbac4557fef31dbdd13fa57bb8ee1ef4b..08bf961fb7278d5dc624a637512bd6c3e4fc5a6e 100644
--- a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php
+++ b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php
@@ -11,7 +11,6 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\CatalogInventory\Api\StockIndexInterface;
 use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
 use Magento\CatalogInventory\Api\StockRegistryInterface;
-use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
 use Magento\Framework\Event\Observer as EventObserver;
 
 class SaveInventoryDataObserver implements ObserverInterface
@@ -36,11 +35,6 @@ class SaveInventoryDataObserver implements ObserverInterface
      */
     protected $stockItemRepository;
 
-    /**
-     * @var StockResolverInterface
-     */
-    protected $stockResolver;
-
     /**
      * @var array
      */
@@ -80,20 +74,17 @@ class SaveInventoryDataObserver implements ObserverInterface
      * @param StockConfigurationInterface $stockConfiguration
      * @param StockRegistryInterface $stockRegistry
      * @param StockItemRepositoryInterface $stockItemRepository
-     * @param StockResolverInterface $stockResolver
      */
     public function __construct(
         StockIndexInterface $stockIndex,
         StockConfigurationInterface $stockConfiguration,
         StockRegistryInterface $stockRegistry,
-        StockItemRepositoryInterface $stockItemRepository,
-        StockResolverInterface $stockResolver
+        StockItemRepositoryInterface $stockItemRepository
     ) {
         $this->stockIndex = $stockIndex;
         $this->stockConfiguration = $stockConfiguration;
         $this->stockRegistry = $stockRegistry;
         $this->stockItemRepository = $stockItemRepository;
-        $this->stockResolver = $stockResolver;
     }
 
     /**
@@ -134,8 +125,7 @@ class SaveInventoryDataObserver implements ObserverInterface
         if (!isset($stockItemData['website_id'])) {
             $stockItemData['website_id'] = $this->stockConfiguration->getDefaultScopeId();
         }
-        $stockItemData['stock_id'] = $this->stockResolver
-            ->getStockId($stockItemData['product_id'], $stockItemData['website_id']);
+        $stockItemData['stock_id'] = $this->stockRegistry->getStock($stockItemData['website_id'])->getStockId();
 
         foreach ($this->paramListToCheck as $dataKey => $configPath) {
             if (null !== $product->getData($configPath['item']) && null === $product->getData($configPath['config'])) {
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Api/StockRegistryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Api/StockRegistryTest.php
index 435defef554e54a259e707d770b9cc894ccba025..02c631727a4b564dfebad116b996c0a078420c39 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Api/StockRegistryTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Api/StockRegistryTest.php
@@ -58,7 +58,6 @@ class StockRegistryTest extends \PHPUnit_Framework_TestCase
     protected $product;
 
     protected $productId = 111;
-    protected $stockId = 112;
     protected $productSku = 'simple';
     protected $websiteId = 111;
 
@@ -136,7 +135,7 @@ class StockRegistryTest extends \PHPUnit_Framework_TestCase
 
     public function testGetStock()
     {
-        $this->assertEquals($this->stock, $this->stockRegistry->getStock($this->stockId));
+        $this->assertEquals($this->stock, $this->stockRegistry->getStock($this->websiteId));
     }
 
     public function testGetStockItem()
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
index 65173103377f4266eb5ce169d52b35863b74cfcd..cb4209de616e798498590461c9db2e3e1891657d 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php
@@ -37,11 +37,6 @@ class StockTest extends \PHPUnit_Framework_TestCase
      */
     protected $statusFactoryMock;
 
-    /**
-     * @var \Magento\CatalogInventory\Model\Spi\StockResolverInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockResolverMock;
-
     protected function setUp()
     {
         $this->stockRegistryProviderMock = $this->getMockBuilder(
@@ -60,26 +55,18 @@ class StockTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->setMethods(['create'])
             ->getMock();
-        $this->stockResolverMock =
-            $this->getMockBuilder('Magento\CatalogInventory\Model\Spi\StockResolverInterface')
-                ->disableOriginalConstructor()
-                ->setMethods(['getStockId'])
-                ->getMock();
         $this->stock = new Stock(
             $this->storeManagerMock,
             $this->scopeConfigMock,
             $this->statusFactoryMock,
-            $this->stockRegistryProviderMock,
-            $this->stockResolverMock
+            $this->stockRegistryProviderMock
         );
     }
 
     public function testAssignStatusToProduct()
     {
-        $websiteId = 0;
-        $productId = 2;
-        $stockId = 3;
-        $status = 1;
+        $websiteId = 1;
+        $status = 'test';
 
         $stockStatusMock = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockStatusInterface')
             ->disableOriginalConstructor()
@@ -106,23 +93,15 @@ class StockTest extends \PHPUnit_Framework_TestCase
         $productMock->expects($this->once())
             ->method('setIsSalable')
             ->with($status);
-        $productMock->expects($this->once())
-            ->method('getId')
-            ->willReturn($productId);
-        $this->stockResolverMock->expects($this->once())
-            ->method('getStockId')
-            ->with($productId, $websiteId)
-            ->willReturn($stockId);
         $this->assertNull($this->stock->assignStatusToProduct($productMock));
     }
 
     public function testAddStockStatusToProducts()
     {
         $storeId = 1;
-        $websiteId = 0;
         $productId = 2;
-        $stockId = 3;
-        $status = 1;
+        $status = 'test';
+
         $productMock = $this->getMockBuilder('Magento\Catalog\Model\Product')
             ->disableOriginalConstructor()
             ->setMethods(['setIsSalable', 'getId'])
@@ -158,21 +137,14 @@ class StockTest extends \PHPUnit_Framework_TestCase
         $storeMock = $this->getMockBuilder('Magento\Store\Model\Store')
             ->disableOriginalConstructor()
             ->getMock();
-        $storeMock->expects($this->once())
-            ->method('getWebsiteId')
-            ->willReturn($websiteId);
         $this->storeManagerMock->expects($this->once())
             ->method('getStore')
-            ->with($storeId)
             ->willReturn($storeMock);
         $this->stockRegistryProviderMock->expects($this->once())
             ->method('getStockStatus')
             ->withAnyParameters()
             ->willReturn($stockStatusMock);
-        $this->stockResolverMock->expects($this->once())
-            ->method('getStockId')
-            ->with($productId, $websiteId)
-            ->willReturn($stockId);
+
         $this->assertNull($this->stock->addStockStatusToProducts($productCollectionMock));
     }
 
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Spi/StockRegistryProviderTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Spi/StockRegistryProviderTest.php
index 5cf08ca76f3a35e8ad7a91ddae0eba033997a957..8328208440b80f2200511934d54fcb1886409035 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Spi/StockRegistryProviderTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Spi/StockRegistryProviderTest.php
@@ -9,20 +9,45 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHe
 
 /**
  * Class StockRegistryProviderTest
+ *
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
  */
 class StockRegistryProviderTest extends \PHPUnit_Framework_TestCase
 {
+    /** @var ObjectManagerHelper */
+    protected $objectManagerHelper;
+
     /**
      * @var \Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $stockRegistryProvider;
 
+    /**
+     * @var \Magento\CatalogInventory\Api\Data\StockInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stock;
+
+    /**
+     * @var \Magento\CatalogInventory\Api\Data\StockItemInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockItem;
+
     /**
      * @var \Magento\CatalogInventory\Api\Data\StockStatusInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $stockStatus;
 
+    /**
+     * @var \Magento\CatalogInventory\Api\Data\StockStatusInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockStatusFactory;
+
+    /**
+     * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockItemFactory;
+
     /**
      * @var \Magento\CatalogInventory\Api\Data\StockInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -58,6 +83,11 @@ class StockRegistryProviderTest extends \PHPUnit_Framework_TestCase
      */
     protected $stockStatusCriteriaFactory;
 
+    /**
+     * @var \Magento\CatalogInventory\Api\StockCriteriaInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockCriteria;
+
     /**
      * @var \Magento\CatalogInventory\Api\StockItemCriteriaInterface|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -68,88 +98,132 @@ class StockRegistryProviderTest extends \PHPUnit_Framework_TestCase
      */
     protected $stockStatusCriteria;
 
-    /**
-     * @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockConfiguration;
+    protected $productId = 111;
+    protected $productSku = 'simple';
+    protected $scopeId = 111;
 
     /**
-     * @var array
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
-    protected $productData = [
-        'stock_id' => 111,
-        'product_id' => 112,
-        'product_sku' => 'simple',
-        'scope_id' => 113
-    ];
-
     protected function setUp()
     {
-        $objectManagerHelper = new ObjectManagerHelper($this);
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
 
-        $this->stockFactory = $this->getMockBuilder('\Magento\CatalogInventory\Api\Data\StockInterfaceFactory')
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
-        $stockItemFactory = $this->getMockBuilder('\Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory')
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
-        $stockStatusFactory = $this->getMockBuilder(
-            '\Magento\CatalogInventory\Api\Data\StockStatusInterfaceFactory'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
-        $stockStatusFactory->expects($this->any())->method('create')->willReturn($this->stockStatus);
-        $this->stockCriteriaFactory = $this->getMockBuilder(
-            'Magento\CatalogInventory\Api\StockCriteriaInterfaceFactory'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
-        $this->stockItemCriteriaFactory = $this->getMockBuilder(
-            'Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
-        $this->stockStatusCriteriaFactory = $this->getMockBuilder(
-            'Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['create'])
-            ->getMock();
+        $this->stock = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\Data\StockInterface',
+            ['__wakeup', 'getStockId'],
+            '',
+            false
+        );
+        $this->stockItem = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\Data\StockItemInterface',
+            ['__wakeup', 'getItemId'],
+            '',
+            false
+        );
+        $this->stockStatus = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\Data\StockStatusInterface',
+            ['__wakeup', 'getProductId'],
+            '',
+            false
+        );
+
+        $this->stockFactory = $this->getMock(
+            '\Magento\CatalogInventory\Api\Data\StockInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockFactory->expects($this->any())->method('create')->willReturn($this->stock);
+
+        $this->stockItemFactory = $this->getMock(
+            '\Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockItemFactory->expects($this->any())->method('create')->willReturn($this->stockItem);
+
+        $this->stockStatusFactory = $this->getMock(
+            '\Magento\CatalogInventory\Api\Data\StockStatusInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockStatusFactory->expects($this->any())->method('create')->willReturn($this->stockStatus);
 
         $this->stockRepository = $this->getMockBuilder('\Magento\CatalogInventory\Api\StockRepositoryInterface')
             ->disableOriginalConstructor()
             ->getMock();
+
         $this->stockItemRepository = $this->getMockBuilder('\Magento\CatalogInventory\Api\StockItemRepositoryInterface')
             ->disableOriginalConstructor()
             ->getMock();
+
         $this->stockStatusRepository = $this->getMockBuilder(
             '\Magento\CatalogInventory\Api\StockStatusRepositoryInterface'
         )
             ->disableOriginalConstructor()
             ->getMock();
-        $this->stockConfiguration = $this->getMockBuilder('Magento\CatalogInventory\Api\StockConfigurationInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getDefaultScopeId'])
-            ->getMockForAbstractClass();
 
-        $this->stockRegistryProvider = $objectManagerHelper->getObject(
+        $this->stockCriteriaFactory = $this->getMock(
+            'Magento\CatalogInventory\Api\StockCriteriaInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockCriteria = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\StockCriteriaInterface',
+            ['setScopeFilter'],
+            '',
+            false
+        );
+
+        $this->stockItemCriteriaFactory = $this->getMock(
+            'Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockItemCriteria = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\StockItemCriteriaInterface',
+            ['setProductsFilter', 'setScopeFilter'],
+            '',
+            false
+        );
+
+        $this->stockStatusCriteriaFactory = $this->getMock(
+            'Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->stockStatusCriteria = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\StockStatusCriteriaInterface',
+            ['setProductsFilter', 'setScopeFilter'],
+            '',
+            false
+        );
+
+        $this->stockRegistryProvider = $this->objectManagerHelper->getObject(
             '\Magento\CatalogInventory\Model\StockRegistryProvider',
             [
                 'stockRepository' => $this->stockRepository,
                 'stockFactory' => $this->stockFactory,
                 'stockItemRepository' => $this->stockItemRepository,
-                'stockItemFactory' => $stockItemFactory,
+                'stockItemFactory' => $this->stockItemFactory,
                 'stockStatusRepository' => $this->stockStatusRepository,
-                'stockStatusFactory' => $stockStatusFactory,
+                'stockStatusFactory' => $this->stockStatusFactory,
+
                 'stockCriteriaFactory' => $this->stockCriteriaFactory,
                 'stockItemCriteriaFactory' => $this->stockItemCriteriaFactory,
-                'stockStatusCriteriaFactory' => $this->stockStatusCriteriaFactory,
-                'stockConfiguration' => $this->stockConfiguration
+                'stockStatusCriteriaFactory' => $this->stockStatusCriteriaFactory
             ]
         );
     }
@@ -159,114 +233,63 @@ class StockRegistryProviderTest extends \PHPUnit_Framework_TestCase
         $this->stockRegistryProvider = null;
     }
 
-    public function testGetStockWithStock()
+    public function testGetStock()
     {
-        $stock = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getStockId'])
-            ->getMockForAbstractClass();
-        $this->stockFactory->expects($this->any())->method('create')->willReturn($stock);
-        $stock->expects($this->once())->method('getStockId')->willReturn($this->productData['stock_id']);
-        $this->stockRepository->expects($this->once())->method('get')->willReturn($stock);
-        $this->assertEquals($stock, $this->stockRegistryProvider->getStock($this->productData['stock_id']));
-    }
-
-    public function testGetStockWithoutStock()
-    {
-        $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')
-            ->willReturn($this->productData['scope_id']);
-        $stock = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getStockId'])
-            ->getMockForAbstractClass();
-        $stock->expects($this->once())->method('getStockId')->willReturn($this->productData['stock_id']);
-        $stockCollection = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockCollectionInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getItems'])
-            ->getMockForAbstractClass();
-        $stockCriteria = $this->getMockBuilder('\Magento\CatalogInventory\Api\StockCriteriaInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getItems'])
-            ->getMockForAbstractClass();
-        $stockCriteria->expects($this->once())->method('setScopeFilter')->with($this->productData['scope_id'])
-            ->willReturnSelf();
-        $this->stockCriteriaFactory->expects($this->once())->method('create')->willReturn($stockCriteria);
-        $this->stockRepository->expects($this->once())->method('getList')
-            ->with($stockCriteria)->willReturn($stockCollection);
-        $stockCollection->expects($this->once())->method('getItems')->willReturn([$stock]);
-        $this->assertEquals($stock, $this->stockRegistryProvider->getStock(null));
+        $this->stockCriteriaFactory->expects($this->once())->method('create')->willReturn($this->stockCriteria);
+        $this->stockCriteria->expects($this->once())->method('setScopeFilter')->willReturn(null);
+        $stockCollection = $this->getMock(
+            '\Magento\CatalogInventory\Model\ResourceModel\Stock\Collection',
+            ['getFirstItem', '__wakeup', 'getItems'],
+            [],
+            '',
+            false
+        );
+        $stockCollection->expects($this->once())->method('getItems')->willReturn([$this->stock]);
+        $this->stockRepository->expects($this->once())->method('getList')->willReturn($stockCollection);
+        $this->stock->expects($this->once())->method('getStockId')->willReturn(true);
+        $this->assertEquals($this->stock, $this->stockRegistryProvider->getStock($this->scopeId));
     }
 
     public function testGetStockItem()
     {
-        $stock = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getStockId'])
-            ->getMockForAbstractClass();
-        $this->stockRepository->expects($this->once())->method('get')->willReturn($stock);
-        $stockItemCriteria = $this->getMockBuilder('Magento\CatalogInventory\Api\StockItemCriteriaInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['setProductsFilter', 'setStockFilter'])
-            ->getMockForAbstractClass();
-        $this->stockItemCriteriaFactory->expects($this->once())->method('create')->willReturn($stockItemCriteria);
-        $stockItemCriteria->expects($this->once())->method('setProductsFilter')->with($this->productData['product_id'])
-            ->willReturnSelf();
-        $stock->expects($this->once())->method('getStockId')->willReturn($this->productData['stock_id']);
-        $stockItemCriteria->expects($this->once())->method('setStockFilter')->with($stock)
-            ->willReturnSelf();
-        $stockItemCollection = $this->getMockBuilder(
-            '\Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Collection'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['getFirstItem', 'getItems'])
-            ->getMock();
-        $stockItem = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockItemInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getItemId'])
-            ->getMockForAbstractClass();
-        $stockItemCollection->expects($this->once())->method('getItems')->willReturn([$stockItem]);
+        $this->stockItemCriteriaFactory->expects($this->once())->method('create')->willReturn($this->stockItemCriteria);
+        $this->stockItemCriteria->expects($this->once())->method('setProductsFilter')->willReturn(null);
+        $stockItemCollection = $this->getMock(
+            '\Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Collection',
+            ['getFirstItem', '__wakeup', 'getItems'],
+            [],
+            '',
+            false
+        );
+        $stockItemCollection->expects($this->once())->method('getItems')->willReturn([$this->stockItem]);
         $this->stockItemRepository->expects($this->once())->method('getList')->willReturn($stockItemCollection);
-        $stockItem->expects($this->once())->method('getItemId')->willReturn(true);
+        $this->stockItem->expects($this->once())->method('getItemId')->willReturn(true);
         $this->assertEquals(
-            $stockItem,
-            $this->stockRegistryProvider->getStockItem($this->productData['product_id'], $this->productData['stock_id'])
+            $this->stockItem,
+            $this->stockRegistryProvider->getStockItem($this->productId, $this->scopeId)
         );
     }
 
     public function testGetStockStatus()
     {
-        $stockStatusCriteria = $this->getMockBuilder('Magento\CatalogInventory\Api\StockStatusCriteriaInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['setProductsFilter', 'addFilter'])
-            ->getMockForAbstractClass();
         $this->stockStatusCriteriaFactory->expects($this->once())
             ->method('create')
-            ->willReturn($stockStatusCriteria);
-        $stockStatusCriteria->expects($this->once())->method('setProductsFilter')
-            ->with($this->productData['product_id'])
-            ->willReturnSelf();
-        $stockStatusCriteria->expects($this->once())->method('addFilter')
-            ->with('stock', 'stock_id', $this->productData['stock_id'])
-            ->willReturnSelf();
-        $stockStatusCollection = $this->getMockBuilder(
-            '\Magento\CatalogInventory\Model\ResourceModel\Stock\Status\Collection'
-        )
-            ->disableOriginalConstructor()
-            ->setMethods(['getFirstItem', 'getItems'])
-            ->getMock();
-        $stockStatus = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockStatusInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getProductId'])
-            ->getMockForAbstractClass();
-        $stockStatusCollection->expects($this->once())->method('getItems')->willReturn([$stockStatus]);
-        $stockStatus->expects($this->once())->method('getProductId')->willReturn($this->productData['product_id']);
+            ->willReturn($this->stockStatusCriteria);
+        $this->stockStatusCriteria->expects($this->once())->method('setScopeFilter')->willReturn(null);
+        $this->stockStatusCriteria->expects($this->once())->method('setProductsFilter')->willReturn(null);
+        $stockStatusCollection = $this->getMock(
+            '\Magento\CatalogInventory\Model\ResourceModel\Stock\Status\Collection',
+            ['getFirstItem', '__wakeup', 'getItems'],
+            [],
+            '',
+            false
+        );
+        $stockStatusCollection->expects($this->once())->method('getItems')->willReturn([$this->stockStatus]);
         $this->stockStatusRepository->expects($this->once())->method('getList')->willReturn($stockStatusCollection);
+        $this->stockStatus->expects($this->once())->method('getProductId')->willReturn($this->productId);
         $this->assertEquals(
-            $stockStatus,
-            $this->stockRegistryProvider->getStockStatus(
-                $this->productData['product_id'],
-                $this->productData['stock_id']
-            )
+            $this->stockStatus,
+            $this->stockRegistryProvider->getStockStatus($this->productId, $this->scopeId)
         );
     }
 }
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
index a81f930ccb6ce51ef9cab945d67498e7b0e46638..27188a0efd3520d18b7e93d83e0485bdd30f64c4 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
@@ -63,9 +63,9 @@ class ItemTest extends \PHPUnit_Framework_TestCase
     protected $resource;
 
     /**
-     * @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Collection|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $stockRegistry;
+    protected $resourceCollection;
 
     /**
      * @var int
@@ -117,8 +117,13 @@ class ItemTest extends \PHPUnit_Framework_TestCase
             false
         );
 
-        $this->stockRegistry = $this->getMockBuilder('Magento\CatalogInventory\Api\StockRegistryInterface')
-            ->getMockForAbstractClass();
+        $this->resourceCollection = $this->getMock(
+            'Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Collection',
+            [],
+            [],
+            '',
+            false
+        );
 
         $this->objectManagerHelper = new ObjectManagerHelper($this);
 
@@ -132,7 +137,7 @@ class ItemTest extends \PHPUnit_Framework_TestCase
                 'stockConfiguration' => $this->stockConfiguration,
                 'stockItemRepository' => $this->stockItemRepository,
                 'resource' => $this->resource,
-                'stockRegistry' => $this->stockRegistry
+                'stockItemRegistry' => $this->resourceCollection
             ]
         );
     }
@@ -452,14 +457,4 @@ class ItemTest extends \PHPUnit_Framework_TestCase
             ],
         ];
     }
-
-    public function testGetStockId()
-    {
-        $stockId = 1;
-        $stock = $this->getMockBuilder('Magento\CatalogInventory\Api\Data\StockInterface')
-            ->getMockForAbstractClass();
-        $this->stockRegistry->expects($this->once())->method('getStock')->willReturn($stock);
-        $stock->expects($this->once())->method('getStockId')->willReturn($stockId);
-        $this->assertEquals($stockId, $this->item->getStockId());
-    }
 }
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddInventoryDataObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddInventoryDataObserverTest.php
index 9688a418119027ae889d39a296aa861fc2f27d5d..68a73ac1eecaae3c6a075d73697a402b3b9a37c8 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddInventoryDataObserverTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddInventoryDataObserverTest.php
@@ -57,24 +57,17 @@ class AddInventoryDataObserverTest extends \PHPUnit_Framework_TestCase
 
     public function testAddInventoryData()
     {
-        $stockStatus = true;
-
         $product = $this->getMockBuilder('Magento\Catalog\Model\Product')
             ->disableOriginalConstructor()
-            ->setMethods(['getStockStatus'])
             ->getMock();
 
-        $product->expects($this->once())
-            ->method('getStockStatus')
-            ->will($this->returnValue($stockStatus));
-
         $this->event->expects($this->once())
             ->method('getProduct')
             ->will($this->returnValue($product));
 
         $this->stockHelper->expects($this->once())
             ->method('assignStatusToProduct')
-            ->with($product, $stockStatus)
+            ->with($product)
             ->will($this->returnSelf());
 
         $this->observer->execute($this->eventObserver);
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php
index 17ca09b6e24fdf54eb566a71890820076b928ce3..96604e75d92946e9cf614139db7ddda4558cec22 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php
@@ -19,26 +19,6 @@ class SaveInventoryDataObserverTest extends \PHPUnit_Framework_TestCase
      */
     protected $stockIndex;
 
-    /**
-     * @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockConfiguration;
-
-    /**
-     * @var \Magento\CatalogInventory\Model\Spi\stockResolverInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockResolver;
-
-    /**
-     * @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockRegistry;
-
-    /**
-     * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $stockItemRepository;
-
     /**
      * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -51,30 +31,12 @@ class SaveInventoryDataObserverTest extends \PHPUnit_Framework_TestCase
 
     protected function setUp()
     {
-        $this->stockIndex = $this->getMockBuilder('Magento\CatalogInventory\Api\StockIndexInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['rebuild'])
-            ->getMockForAbstractClass();
-
-        $this->stockConfiguration = $this->getMockBuilder('Magento\CatalogInventory\Api\StockConfigurationInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getDefaultScopeId'])
-            ->getMockForAbstractClass();
-
-        $this->stockResolver = $this->getMockBuilder('Magento\CatalogInventory\Model\Spi\StockResolverInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getStockId'])
-            ->getMockForAbstractClass();
-
-        $this->stockRegistry = $this->getMockBuilder('Magento\CatalogInventory\Api\StockRegistryInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['getStockItem'])
-            ->getMockForAbstractClass();
-
-        $this->stockItemRepository = $this->getMockBuilder('Magento\CatalogInventory\Api\StockItemRepositoryInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['save'])
-            ->getMockForAbstractClass();
+        $this->stockIndex = $this->getMockForAbstractClass(
+            'Magento\CatalogInventory\Api\StockIndexInterface',
+            ['rebuild'],
+            '',
+            false
+        );
 
         $this->event = $this->getMockBuilder('Magento\Framework\Event')
             ->disableOriginalConstructor()
@@ -94,15 +56,11 @@ class SaveInventoryDataObserverTest extends \PHPUnit_Framework_TestCase
             'Magento\CatalogInventory\Observer\SaveInventoryDataObserver',
             [
                 'stockIndex' => $this->stockIndex,
-                'stockConfiguration' => $this->stockConfiguration,
-                'stockResolver' => $this->stockResolver,
-                'stockRegistry' => $this->stockRegistry,
-                'stockItemRepository' => $this->stockItemRepository
             ]
         );
     }
 
-    public function testSaveInventoryDataWithoutStockData()
+    public function testSaveInventoryData()
     {
         $productId = 4;
         $websiteId = 5;
@@ -134,49 +92,4 @@ class SaveInventoryDataObserverTest extends \PHPUnit_Framework_TestCase
 
         $this->observer->execute($this->eventObserver);
     }
-
-    public function testSaveInventoryDataWithStockData()
-    {
-        $stockItemData = [
-            'qty' => 4,
-            'product_id' => 2,
-            'website_id' => 3,
-            'stock_id' => 1,
-            'qty_correction' => -1
-        ];
-
-        $product = $this->getMock(
-            'Magento\Catalog\Model\Product',
-            ['getStockData', 'getId', 'getData'],
-            [],
-            '',
-            false
-        );
-        $product->expects($this->exactly(2))->method('getStockData')->will($this->returnValue(
-            ['qty' => $stockItemData['qty']]
-        ));
-        $product->expects($this->once())->method('getId')->will($this->returnValue($stockItemData['product_id']));
-        $product->expects($this->any())->method('getData')->willReturnMap(
-            [
-                ['stock_data/original_inventory_qty', null, $stockItemData['qty']+1]
-            ]
-        );
-        $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')
-            ->willReturn($stockItemData['website_id']);
-        $this->stockResolver->expects($this->once())->method('getStockId')
-            ->with($stockItemData['product_id'], $stockItemData['website_id'])
-            ->willReturn($stockItemData['stock_id']);
-        $stockItem = $this->getMockBuilder('\Magento\CatalogInventory\Api\Data\StockItemInterface')
-            ->disableOriginalConstructor()
-            ->setMethods(['addData'])
-            ->getMockForAbstractClass();
-        $this->stockRegistry->expects($this->once())->method('getStockItem')
-            ->with($stockItemData['product_id'], $stockItemData['website_id'])
-            ->willReturn($stockItem);
-        $stockItem->expects($this->once())->method('addData')->with($stockItemData)->willReturnSelf();
-        $this->stockItemRepository->expects($this->once())->method('save')->with($stockItem);
-        $this->event->expects($this->once())->method('getProduct')->will($this->returnValue($product));
-
-        $this->observer->execute($this->eventObserver);
-    }
 }
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index 40f9bf2d1275d44e8822cd021fd9610b00ad71c6..fbce94514ebff09f03060a95ca5bea4a5c698fcf 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -30,8 +30,7 @@
 
     <preference for="Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface" type="Magento\CatalogInventory\Model\StockRegistryProvider" />
     <preference for="Magento\CatalogInventory\Model\Spi\StockStateProviderInterface" type="Magento\CatalogInventory\Model\StockStateProvider" />
-    <preference for="Magento\CatalogInventory\Model\Spi\StockResolverInterface" type="Magento\CatalogInventory\Model\StockResolver" />
-
+    
     <preference for="Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface" type="\Magento\CatalogInventory\Model\ResourceModel\Stock" />
     <type name="Magento\Catalog\Model\Product\Attribute\Repository">
         <plugin name="filterCustomAttribute" type="Magento\CatalogInventory\Model\Plugin\FilterCustomAttribute" />
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
index ca218cf2ea5b20eaef4a636e2506898ccb9931e9..833ca223a7b9b7ebf1ccc0cde42de3b63e5a166a 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php
@@ -30,20 +30,35 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
      */
     protected $relation;
 
+    /**
+     * @var \Magento\Framework\Model\Entity\MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadataPool;
+
     protected function setUp()
     {
         $connectionMock = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface')->getMock();
 
         $this->resource = $this->getMock('Magento\Framework\App\ResourceConnection', [], [], '', false);
         $this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($connectionMock));
+
         $this->relation = $this->getMock('Magento\Catalog\Model\ResourceModel\Product\Relation', [], [], '', false);
 
+        $metadata = $this->getMock('Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false);
+
+        $this->metadataPool = $this->getMock('Magento\Framework\Model\Entity\MetadataPool', [], [], '', false);
+        $this->metadataPool->expects($this->any())
+            ->method('getMetadata')
+            ->with(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->willReturn($metadata);
+
         $this->objectManagerHelper = new ObjectManagerHelper($this);
         $this->configurable = $this->objectManagerHelper->getObject(
             'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable',
             [
                 'resource' => $this->resource,
-                'catalogProductRelation' => $this->relation
+                'catalogProductRelation' => $this->relation,
+                'metadataPool' => $this->metadataPool
             ]
         );
     }
diff --git a/app/code/Magento/Downloadable/view/adminhtml/web/downloadable-type-handler.js b/app/code/Magento/Downloadable/view/adminhtml/web/downloadable-type-handler.js
index 12521c18ca6c17c335f758c82311832dcc6ef2ae..e8e61c26b49cfb7e318d712e65b05f920c0a0f73 100644
--- a/app/code/Magento/Downloadable/view/adminhtml/web/downloadable-type-handler.js
+++ b/app/code/Magento/Downloadable/view/adminhtml/web/downloadable-type-handler.js
@@ -65,7 +65,10 @@ define([
             if (productType.type.current === 'downloadable') {
                 weight.change(false);
                 weight.$weightSwitcher().one('change', function () {
-                    $(document).trigger('setTypeProduct', null);
+                    $(document).trigger(
+                        'setTypeProduct',
+                        productType.type.init === 'downloadable' ? 'virtual' : productType.type.init
+                    );
                 });
                 this.show();
             } else {
diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml
index d716794f5ca8e67649354ac71a070af028661239..545dc858eee17321cf00ff06e79d73277a8c2707 100644
--- a/app/code/Magento/Paypal/etc/adminhtml/system.xml
+++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml
@@ -51,6 +51,7 @@
                 <group id="wpp_usuk" sortOrder="40" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout">
                     <label>Payments Pro (Includes Express Checkout)</label>
                     <attribute type="activity_path">payment/paypal_payment_pro/active</attribute>
+                    <more_url>https://www.paypal.com/us/webapps/mpp/paypal-payments-pro?partner_id=NB9WWHYEMVUMS</more_url>
                     <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10">
                         <field id="enable_paypal_payflow">
                             <attribute type="shared">0</attribute>
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
new file mode 100644
index 0000000000000000000000000000000000000000..b5373ff1648b7f2c3db97e8333551781c09b09e1
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/form/components/button.js
@@ -0,0 +1,103 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+    'uiElement',
+    'uiRegistry',
+    'uiLayout',
+    'mageUtils'
+], function (Element, registry, layout, utils) {
+    'use strict';
+
+    return Element.extend({
+        defaults: {
+            additionalClasses: {},
+            displayArea: 'outsideGroup',
+            displayAsLink: false,
+            elementTmpl: 'ui/form/element/button',
+            template: 'ui/form/components/button/simple'
+        },
+
+        /**
+         * Initializes component.
+         *
+         * @returns {Object} Chainable.
+         */
+        initialize: function () {
+            return this._super()
+                ._setClasses();
+        },
+
+        /**
+         * Performs configured actions
+         */
+        action: function () {
+            this.actions.forEach(this.applyAction, this);
+        },
+
+        /**
+         * Apply action on target component,
+         * but previously create this component from template if it is not existed
+         *
+         * @param {Object} action - action configuration
+         */
+        applyAction: function (action) {
+            var targetName = action.targetName,
+                params = action.params,
+                actionName = action.actionName,
+                target;
+
+            if (!registry.has(targetName)) {
+                this.getFromTemplate(targetName);
+            }
+            target = registry.async(targetName);
+
+            if (target && typeof target === 'function' && actionName) {
+                target(actionName, params);
+            }
+        },
+
+        /**
+         * Create target component from template
+         *
+         * @param {Object} targetName - name of component,
+         * that supposed to be a template and need to be initialized
+         */
+        getFromTemplate: function (targetName) {
+            var parentName = targetName.split('.'),
+                index = parentName.pop(),
+                child;
+
+            parentName = parentName.join('.');
+            child = utils.template({
+                parent: parentName,
+                name: index,
+                nodeTemplate: targetName
+            });
+            layout([child]);
+        },
+
+        /**
+         * Extends 'additionalClasses' object.
+         *
+         * @returns {Object} Chainable.
+         */
+        _setClasses: function () {
+            if (typeof this.additionalClasses === 'string') {
+                this.additionalClasses = this.additionalClasses
+                    .trim()
+                    .split(' ')
+                    .reduce(function (classes, name) {
+                        classes[name] = true;
+
+                        return classes;
+                    }, {}
+                );
+            }
+
+            return this;
+        }
+    });
+});
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js b/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js
index c769be3775f46ddba47d5d9110974fc2bd5782da..1ac9b45a46e86a9df232db8993932a886c3bcd8c 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/core/element/links.js
@@ -51,7 +51,7 @@ define([
         }
 
         if (owner.component !== target.component) {
-            value = utils.copy(value);
+            value = data.inversionValue ? !utils.copy(value) : utils.copy(value);
         }
 
         component.set(property, value);
@@ -149,6 +149,11 @@ define([
     function transfer(owner, data) {
         var args = _.toArray(arguments);
 
+        if (data.target.substr(0,1) === '!') {
+            data.target = data.target.substr(1);
+            data.inversionValue = true;
+        }
+
         if (owner.name === data.target) {
             args.unshift(owner);
 
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js
index 839a87481a38631267f295c9908abf2873d74f4d..7fa3ff22af8975b62d23b3f9c07ff3ab8d813c39 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/outer_click.js
@@ -15,6 +15,30 @@ define([
         onlyIfVisible: true
     };
 
+    /**
+     * Checks if element sis visible.
+     *
+     * @param {Element} el
+     * @returns {Boolean}
+     */
+    function isVisible(el) {
+        var style = window.getComputedStyle(el),
+            visibility = {
+                display: 'none',
+                visibility: 'hidden',
+                opacity: '0'
+            },
+            visible = true;
+
+        _.each(visibility, function (val, key) {
+            if (style[key] === val) {
+                visible = false;
+            }
+        });
+
+        return visible;
+    }
+
     /**
      * Document click handler which in case if event target is not
      * a descendant of provided container element,
@@ -33,7 +57,7 @@ define([
         }
 
         if (config.onlyIfVisible) {
-            if (!_.isNull(container.offsetParent)) {
+            if (!_.isNull(container.offsetParent) && isVisible(container)) {
                 callback();
             }
         } else {
diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js
new file mode 100644
index 0000000000000000000000000000000000000000..cccf95e00bf54f2a667e85fab4d5f12b435728a5
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js
@@ -0,0 +1,315 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+    'Magento_Ui/js/lib/view/utils/async',
+    'uiCollection',
+    'uiRegistry',
+    'underscore',
+    './modal'
+], function ($, Collection, registry, _) {
+    'use strict';
+
+    return Collection.extend({
+        defaults: {
+            template: 'ui/modal/modal-component',
+            options: {
+                title: '',
+                buttons: [],
+                keyEventHandlers: {}
+            },
+            valid: true,
+            listens: {
+                state: 'onState'
+            },
+            modalClass: 'modal-component'
+        },
+
+        /**
+         * Initializes component.
+         *
+         * @returns {Object} Chainable.
+         */
+        initialize: function () {
+            this._super();
+            _.bindAll(this,
+                'initModal',
+                'openModal',
+                'closeModal',
+                'toggleModal',
+                'setPrevValues',
+                'actionCancel',
+                'validate');
+            this.initializeContent();
+
+            return this;
+        },
+
+        /**
+         * Initializes modal configuration
+         *
+         * @returns {Object} Chainable.
+         */
+        initConfig: function () {
+            return this._super()
+                .initSelector()
+                .initModalEvents();
+        },
+
+        /**
+         * Configure modal selector
+         *
+         * @returns {Object} Chainable.
+         */
+        initSelector: function () {
+            this.contentSelector = '.' + this.modalClass;
+            this.options.modalClass = this.name.replace(/\./g, '_');
+            this.rootSelector = '.' + this.options.modalClass;
+
+            return this;
+        },
+
+        /**
+         * Configure modal keyboard handlers
+         * and outer click
+         *
+         * @returns {Object} Chainable.
+         */
+        initModalEvents: function () {
+            this.options.keyEventHandlers.escapeKey = this.options.outerClickHandler = this.actionCancel.bind(this);
+
+            return this;
+        },
+
+        /**
+         * Initialize modal's content components
+         */
+        initializeContent: function () {
+            $.async(this.contentSelector, this, this.initModal);
+        },
+
+        /**
+         * Init toolbar section so other components will be able to place something in it
+         */
+        initToolbarSection: function () {
+            this.set('toolbarSection', this.modal.data('modal').modal.find('header').get(0));
+        },
+
+        /**
+         * Initializes observable properties.
+         *
+         * @returns {Object} Chainable.
+         */
+        initObservable: function () {
+            this._super();
+            this.observe('state');
+
+            return this;
+        },
+
+        /**
+         * Wrap content in a modal of certain type
+         *
+         * @param {HTMLElement} element
+         * @returns {Object} Chainable.
+         */
+        initModal: function (element) {
+            if (!this.modal) {
+                this.overrideModalButtonCallback();
+                this.options.modalCloseBtnHandler = this.actionCancel;
+                this.modal = $(element).modal(this.options);
+                this.initToolbarSection();
+
+                if (this.waitCbk) {
+                    this.waitCbk();
+                    this.waitCbk = null;
+                }
+            }
+
+            return this;
+        },
+
+        /**
+         * Open modal
+         */
+        openModal: function () {
+            if (this.modal) {
+                this.state(true);
+            } else {
+                this.waitCbk = this.openModal;
+            }
+        },
+
+        /**
+         * Close modal
+         */
+        closeModal: function () {
+            if (this.modal) {
+                this.state(false);
+            } else {
+                this.waitCbk = this.closeModal;
+            }
+        },
+
+        /**
+         * Toggle modal
+         */
+        toggleModal: function () {
+            if (this.modal) {
+                this.state(!this.state());
+            } else {
+                this.waitCbk = this.toggleModal;
+            }
+        },
+
+        /**
+         * Wrap content in a modal of certain type
+         *
+         * @param {Boolean} state
+         */
+        onState: function (state) {
+            if (state) {
+                this.modal.modal('openModal');
+                this.applyData();
+            } else {
+                this.modal.modal('closeModal');
+            }
+        },
+
+        /**
+         * Validate everything validatable in modal
+         */
+        validate: function (elem) {
+            if (typeof elem.validate === 'function') {
+                this.valid = this.valid & elem.validate().valid;
+            } else if (elem.elems) {
+                elem.elems().forEach(this.validate, this);
+            }
+        },
+
+        /**
+         * Reset data from provider
+         */
+        resetData: function () {
+            this.elems().forEach(this.resetValue, this);
+        },
+
+        /**
+         * Update 'applied' property with data from modal content
+         */
+        applyData: function () {
+            var applied = {};
+
+            this.elems().forEach(this.gatherValues.bind(this, applied), this);
+            this.applied = applied;
+        },
+
+        /**
+         * Gather values from modal content
+         *
+         * @param {Array} applied
+         * @param {HTMLElement} elem
+         */
+        gatherValues: function (applied, elem) {
+            if (typeof elem.value === 'function') {
+                applied[elem.index] = elem.value();
+            } else if (elem.elems) {
+                elem.elems().forEach(this.gatherValues.bind(this, applied), this);
+            }
+        },
+
+        /**
+         * Set to previous values from modal content
+         *
+         * @param {HTMLElement} elem
+         */
+        setPrevValues: function (elem) {
+            if (typeof elem.value === 'function') {
+                this.modal.focus();
+                elem.value(this.applied[elem.index]);
+            } else if (elem.elems) {
+                elem.elems().forEach(this.setPrevValues, this);
+            }
+        },
+
+        /**
+         * Triggers some method in every modal child elem, if this method is defined
+         *
+         * @param {Object} action - action configuration,
+         * must contain actionName and targetName and
+         * can contain params
+         */
+        triggerAction: function (action) {
+            var targetName = action.targetName,
+                params = action.params,
+                actionName = action.actionName,
+                target;
+
+            target = registry.async(targetName);
+
+            if (target && typeof target === 'function' && actionName) {
+                target(actionName, params);
+            }
+        },
+
+        /**
+         * Override modal buttons callback placeholders with real callbacks
+         */
+        overrideModalButtonCallback: function () {
+            var buttons = this.options.buttons;
+
+            if (buttons && buttons.length) {
+                buttons.forEach(function (button) {
+                    button.click = this.getButtonClickHandler(button.actions);
+                }, this);
+            }
+        },
+
+        /**
+         * Generate button click handler based on button's 'actions' configuration
+         */
+        getButtonClickHandler: function (actionsConfig) {
+            var actions = actionsConfig.map(
+                function (actionConfig) {
+                    if (_.isObject(actionConfig)) {
+                        return this.triggerAction.bind(this, actionConfig);
+                    }
+
+                    return this[actionConfig] ? this[actionConfig].bind(this) : function () {};
+                }, this);
+
+            return function () {
+                actions.forEach(
+                    function (action) {
+                        action();
+                    }
+                );
+            };
+        },
+
+        /**
+         * Cancels changes in modal:
+         * returning elems values to the previous state,
+         * and close modal
+         */
+        actionCancel: function () {
+            this.elems().forEach(this.setPrevValues, this);
+            this.closeModal();
+        },
+
+        /**
+         * Accept changes in modal by not preventing them.
+         * Can be extended by exporting 'gatherValues' result somewhere
+         */
+        actionDone: function () {
+            this.valid = true;
+            this.elems().forEach(this.validate, this);
+
+            if (this.valid) {
+                this.closeModal();
+            }
+        }
+    });
+});
diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js
index 23011d607cdadc301ee6cf4cebeb148b400c0260..5dcfc53459baad6607edce1f13be49e708ead692 100644
--- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js
+++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js
@@ -118,13 +118,14 @@ define([
                 'closeModal'
             );
 
+            _.extend(this.keyEventHandlers, this.options.keyEventHandlers);
             this.options.transitionEvent = transitionEvent;
             this._createWrapper();
             this._renderModal();
             this._createButtons();
             $(this.options.trigger).on('click', _.bind(this.toggleModal, this));
             this._on(this.modal.find(this.options.modalCloseBtn), {
-                'click': this.closeModal
+                'click': this.options.modalCloseBtnHandler ? this.options.modalCloseBtnHandler : this.closeModal
             });
             this._on(this.element, {
                 'openModal': this.openModal,
@@ -374,7 +375,8 @@ define([
          * Creates overlay, append it to wrapper, set previous click event on overlay.
          */
         _createOverlay: function () {
-            var events;
+            var events,
+                outerClickHandler = this.options.outerClickHandler || this.closeModal;
 
             this.overlay = $('.' + this.options.overlayClass);
 
@@ -386,7 +388,7 @@ define([
             }
             events = $._data(this.overlay.get(0), 'events');
             events ? this.prevOverlayHandler = events.click[0].handler : false;
-            this.options.clickableOverlay ? this.overlay.unbind().on('click', this.closeModal) : false;
+            this.options.clickableOverlay ? this.overlay.unbind().on('click', outerClickHandler) : false;
         },
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html b/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html
new file mode 100644
index 0000000000000000000000000000000000000000..d562e699c843a584fabd5f35905ca5e92ef8ad96
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/form/components/button/simple.html
@@ -0,0 +1,7 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<render args="elementTmpl"/>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/button.html b/app/code/Magento/Ui/view/base/web/templates/form/element/button.html
new file mode 100644
index 0000000000000000000000000000000000000000..8f337ce7acb966b57b511b1535ceedb6117cdc03
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/form/element/button.html
@@ -0,0 +1,14 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<button type="button"
+        css="
+            'action-advanced': $data.displayAsLink,
+            'action-secondary': !$data.displayAsLink
+        "
+        click="action"
+        text="title">
+</button>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e39c27e0741232496319e1385a667b181ee623f7
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html
@@ -0,0 +1,12 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+<div data-bind="css: modalClass">
+    <!-- ko foreach: { data: elems, as: 'element' } -->
+    <!-- ko template: element.getTemplate() --><!-- /ko -->
+    <!-- /ko -->
+</div>
\ No newline at end of file
diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml b/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml
index 5c27c009b445158c3714fb1464a0c68234537211..420638d5a8ea77aa27b603ba4f8fcdc9d6f4db76 100644
--- a/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml
+++ b/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml
@@ -13,14 +13,14 @@
     <div class="content" style="clear: both;">
         <input type="hidden" name="categories" id="product_categories" value="" />
         <?php if ($block->getRoot()): ?>
-        <div data-mage-init='<?php
+        <div data-mage-init="<?php
             echo $block->escapeHtml($this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode([
                 'categoryTree' => [
                     'data' => $block->getTreeArray(null),
                     'url' => $block->getLoadTreeUrl(),
                 ],
             ]));
-        ?>' class="jstree-default"></div>
+        ?>" class="jstree-default"></div>
         <?php endif; ?>
     </div>
 </fieldset>
diff --git a/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml
index 135880f5f314214efdc4d95540f489a5fc7bd8fe..89d2b75a3b882322a91f953208242673130ae5b9 100644
--- a/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml
+++ b/app/code/Magento/Widget/view/adminhtml/templates/catalog/category/widget/tree.phtml
@@ -10,7 +10,7 @@
 
 <?php $_divId = 'tree' . $block->getId() ?>
 <div id="<?php /* @escapeNotVerified */ echo $_divId ?>" class="tree"></div>
-<script id="ie-deferred-loader" defer="defer" src=""></script>
+<script id="ie-deferred-loader" defer="defer" src="//:"></script>
 <script>
 require(['jquery', "prototype", "extjs/ext-tree-checkbox"], function(jQuery){
 
diff --git a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
index 6831ca32a007b1c5abe9f67b322e76fe5ee38749..a98b8df1fd5deb0e3b50ee395f4a9102cca68083 100644
--- a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
+++ b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
@@ -15,7 +15,7 @@
         <div class="actions"><?php echo $block->getAddLayoutButtonHtml() ?></div>
     </div>
 </fieldset>
-<script id="ie-deferred-loader" defer="defer" src=""></script>
+<script id="ie-deferred-loader" defer="defer" src="//:"></script>
 <script>
 require([
     'jquery',
diff --git a/app/etc/di.xml b/app/etc/di.xml
index f19b5e2aa44af740cbf47989ab09d56a3bc45296..c742aa25af54173424754282c1be8337b2defd7b 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -142,6 +142,8 @@
     <preference for="Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface" type="Magento\Framework\Stdlib\DateTime\DateTimeFormatter"/>
     <preference for="Magento\Framework\Api\Search\SearchInterface" type="Magento\Framework\Search\Search"/>
     <preference for="Magento\Framework\View\Design\FileResolution\Fallback\ResolverInterface" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple" />
+    <preference for="Cm\RedisSession\Handler\ConfigInterface" type="Magento\Framework\Session\SaveHandler\Redis\Config"/>
+    <preference for="Cm\RedisSession\Handler\LoggerInterface" type="Magento\Framework\Session\SaveHandler\Redis\Logger"/>
     <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" />
     <type name="Magento\Framework\Logger\Handler\Base">
         <arguments>
@@ -187,9 +189,16 @@
         <arguments>
             <argument name="handlers" xsi:type="array">
                 <item name="db" xsi:type="string">Magento\Framework\Session\SaveHandler\DbTable</item>
+                <item name="redis" xsi:type="string">Magento\Framework\Session\SaveHandler\Redis</item>
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Session\SaveHandler\Redis">
+        <arguments>
+            <argument name="config" xsi:type="object">Cm\RedisSession\Handler\ConfigInterface</argument>
+            <argument name="logger" xsi:type="object">Cm\RedisSession\Handler\LoggerInterface</argument>
+        </arguments>
+    </type>
     <virtualType name="interceptionConfigScope" type="Magento\Framework\Config\Scope">
         <arguments>
             <argument name="defaultScope" xsi:type="string">global</argument>
diff --git a/composer.json b/composer.json
index 2690ac011a4658852e9dff9a592a5aa9524574fe..6de9572c2a9586377f036c0f813c0801f51f0b3c 100644
--- a/composer.json
+++ b/composer.json
@@ -32,6 +32,8 @@
         "zendframework/zend-log": "~2.4.6",
         "zendframework/zend-http": "~2.4.6",
         "magento/zendframework1": "1.12.16",
+        "colinmollenhour/credis": "1.6",
+        "colinmollenhour/php-redis-session-abstract": "1.0",
         "composer/composer": "1.0.0-alpha10",
         "monolog/monolog": "1.16.0",
         "oyejorge/less.php": "1.7.0.3",
@@ -183,7 +185,6 @@
         "magento/framework": "100.0.2",
         "trentrichardson/jquery-timepicker-addon": "1.4.3",
         "colinmollenhour/cache-backend-redis": "1.8",
-        "colinmollenhour/credis": "1.5",
         "components/jquery": "1.11.0",
         "blueimp/jquery-file-upload": "5.6.14",
         "components/jqueryui": "1.10.4",
@@ -194,7 +195,6 @@
         "component_paths": {
             "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js",
             "colinmollenhour/cache-backend-redis": "lib/internal/Cm/Cache/Backend/Redis.php",
-            "colinmollenhour/credis": "lib/internal/Credis",
             "components/jquery": [
                 "lib/web/jquery.js",
                 "lib/web/jquery/jquery.min.js",
diff --git a/composer.lock b/composer.lock
index 7fefee4db023190b62542ce48d8316a78d0856cc..a8c2a9acb5732af76eb4153a39bf27b61eb750f8 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": "80d81327a228d96ed4512c92e09d5b02",
-    "content-hash": "7a457b69136c2954644691bd92ca7cc6",
+    "hash": "14cde9a911c97dfb8b8d65aa2fc37a9f",
+    "content-hash": "3ca6f21142f50665ed50595bfb546c41",
     "packages": [
         {
             "name": "braintree/braintree_php",
@@ -54,6 +54,83 @@
             "description": "Braintree PHP Client Library",
             "time": "2015-11-19 19:14:47"
         },
+        {
+            "name": "colinmollenhour/credis",
+            "version": "1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/colinmollenhour/credis.git",
+                "reference": "409edfd0ea81f5cb74afbdb86df54890c207b5e4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/409edfd0ea81f5cb74afbdb86df54890c207b5e4",
+                "reference": "409edfd0ea81f5cb74afbdb86df54890c207b5e4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "Client.php",
+                    "Cluster.php",
+                    "Sentinel.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Colin Mollenhour",
+                    "email": "colin@mollenhour.com"
+                }
+            ],
+            "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
+            "homepage": "https://github.com/colinmollenhour/credis",
+            "time": "2015-11-28 01:20:04"
+        },
+        {
+            "name": "colinmollenhour/php-redis-session-abstract",
+            "version": "v1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/colinmollenhour/php-redis-session-abstract.git",
+                "reference": "1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9",
+                "reference": "1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9",
+                "shasum": ""
+            },
+            "require": {
+                "colinmollenhour/credis": "1.6",
+                "magento/zendframework1": "1.12.16",
+                "php": "~5.5.0|~5.6.0|~7.0.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Cm\\RedisSession\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin Mollenhour"
+                }
+            ],
+            "description": "A Redis-based session handler with optimistic locking",
+            "homepage": "https://github.com/colinmollenhour/php-redis-session-abstract",
+            "time": "2016-01-14 16:04:27"
+        },
         {
             "name": "composer/composer",
             "version": "1.0.0-alpha10",
@@ -720,7 +797,7 @@
         },
         {
             "name": "symfony/console",
-            "version": "v2.6.12",
+            "version": "v2.6.13",
             "target-dir": "Symfony/Component/Console",
             "source": {
                 "type": "git",
@@ -778,16 +855,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": {
@@ -834,20 +911,20 @@
             ],
             "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",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "dd41ae57f4f737be271d944a0cc5f5f21203a7c6"
+                "reference": "c90fabdd97e431ee19b6383999cf35334dff27da"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/dd41ae57f4f737be271d944a0cc5f5f21203a7c6",
-                "reference": "dd41ae57f4f737be271d944a0cc5f5f21203a7c6",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/c90fabdd97e431ee19b6383999cf35334dff27da",
+                "reference": "c90fabdd97e431ee19b6383999cf35334dff27da",
                 "shasum": ""
             },
             "require": {
@@ -883,20 +960,20 @@
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-05 11:09:21"
+            "time": "2016-01-14 08:26:52"
         },
         {
             "name": "symfony/process",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "62c254438b5040bc2217156e1570cf2206e8540c"
+                "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/62c254438b5040bc2217156e1570cf2206e8540c",
-                "reference": "62c254438b5040bc2217156e1570cf2206e8540c",
+                "url": "https://api.github.com/repos/symfony/process/zipball/6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac",
+                "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac",
                 "shasum": ""
             },
             "require": {
@@ -932,7 +1009,7 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-23 11:03:46"
+            "time": "2016-01-06 09:59:23"
         },
         {
             "name": "tedivm/jshrink",
@@ -2581,16 +2658,16 @@
         },
         {
             "name": "fabpot/php-cs-fixer",
-            "version": "v1.11",
+            "version": "v1.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
-                "reference": "bd3ec2c2b774e0e127ac2c737ec646d9cf2f9eef"
+                "reference": "2c9f8298181f059c5077abda78019b9a0c9a7cc0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/bd3ec2c2b774e0e127ac2c737ec646d9cf2f9eef",
-                "reference": "bd3ec2c2b774e0e127ac2c737ec646d9cf2f9eef",
+                "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/2c9f8298181f059c5077abda78019b9a0c9a7cc0",
+                "reference": "2c9f8298181f059c5077abda78019b9a0c9a7cc0",
                 "shasum": ""
             },
             "require": {
@@ -2631,7 +2708,7 @@
                 }
             ],
             "description": "A tool to automatically fix PHP code style",
-            "time": "2015-12-01 22:34:33"
+            "time": "2016-01-20 19:00:28"
         },
         {
             "name": "league/climate",
@@ -3672,16 +3749,16 @@
         },
         {
             "name": "symfony/config",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "17d4b2e64ce1c6ba7caa040f14469b3c44d7f7d2"
+                "reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/17d4b2e64ce1c6ba7caa040f14469b3c44d7f7d2",
-                "reference": "17d4b2e64ce1c6ba7caa040f14469b3c44d7f7d2",
+                "url": "https://api.github.com/repos/symfony/config/zipball/41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
+                "reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
                 "shasum": ""
             },
             "require": {
@@ -3718,20 +3795,20 @@
             ],
             "description": "Symfony Config Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-26 13:37:56"
+            "time": "2016-01-03 15:33:41"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "c5086d186f538c2711b9af6f727be7b0446979cd"
+                "reference": "ba94a914e244e0d05f0aaef460d5558d5541d2b1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c5086d186f538c2711b9af6f727be7b0446979cd",
-                "reference": "c5086d186f538c2711b9af6f727be7b0446979cd",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ba94a914e244e0d05f0aaef460d5558d5541d2b1",
+                "reference": "ba94a914e244e0d05f0aaef460d5558d5541d2b1",
                 "shasum": ""
             },
             "require": {
@@ -3780,20 +3857,20 @@
             ],
             "description": "Symfony DependencyInjection Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-26 13:37:56"
+            "time": "2016-01-12 17:46:01"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc"
+                "reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/a7ad724530a764d70c168d321ac226ba3d2f10fc",
-                "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/637b64d0ee10f44ae98dbad651b1ecdf35a11e8c",
+                "reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c",
                 "shasum": ""
             },
             "require": {
@@ -3829,7 +3906,7 @@
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-22 10:25:57"
+            "time": "2016-01-13 10:28:07"
         },
         {
             "name": "symfony/stopwatch",
@@ -3882,16 +3959,16 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966"
+                "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966",
-                "reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8",
+                "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8",
                 "shasum": ""
             },
             "require": {
@@ -3927,7 +4004,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2015-12-26 13:37:56"
+            "time": "2016-01-13 10:28:07"
         }
     ],
     "aliases": [],
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
index 86ae1d8d66a067591d0fae2bed7b35c58f068663..35250af124192cfa13a2222eae81f2c90f141030 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
@@ -1,53 +1,130 @@
 <?php
 /**
- * Store configuration edit form
+ * Store configuration edit form.
  *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
 namespace Magento\Backend\Test\Block\System\Config;
 
-use Magento\Mtf\Block\Block;
 use Magento\Mtf\Factory\Factory;
+use Magento\Mtf\Block\BlockFactory;
+use Magento\Mtf\Block\Block;
+use Magento\Mtf\Client\ElementInterface;
+use Magento\Mtf\Client\BrowserInterface;
 use Magento\Mtf\Client\Locator;
 
+/**
+ * Class Form.
+ */
 class Form extends Block
 {
     /**
-     * Group block
+     * Group block selector.
+     *
+     * @var string
+     */
+    protected $groupBlock = '.section-config.active #%s_%s';
+
+    /**
+     * Group block link selector.
      *
      * @var string
      */
-    protected $groupBlock = '//legend[contains(text(), "%s")]/../..';
+    protected $groupBlockLink = '#%s_%s-head';
 
     /**
-     * Save button
+     * Save button selector.
      *
      * @var string
      */
-    protected $saveButton = '//button[@data-ui-id="system-config-edit-save-button"]';
+    protected $saveButton = '#save';
 
     /**
-     * Retrieve store configuration form group
+     *  Tab content readiness.
      *
-     * @param string $name
+     * @var string
+     */
+    protected $tabReadiness = '.admin__page-nav-item._active._loading';
+
+    /**
+     *  Url associated with the form.
+     *
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * @constructor
+     * @param ElementInterface $element
+     * @param BlockFactory $blockFactory
+     * @param BrowserInterface $browser
+     * @param array $config
+     */
+    public function __construct(
+        ElementInterface $element,
+        BlockFactory $blockFactory,
+        BrowserInterface $browser,
+        array $config = []
+    ) {
+        parent::__construct($element, $blockFactory, $browser, $config);
+        $this->baseUrl = $this->browser->getUrl();
+        if (substr($this->baseUrl, -1) !== '/') {
+            $this->baseUrl = $this->baseUrl . '/';
+        }
+    }
+
+    /**
+     * Obtain store configuration form group.
+     *
+     * @param string $tabName
+     * @param string $groupName
      * @return Form\Group
      */
-    public function getGroup($name)
+    public function getGroup($tabName, $groupName)
     {
-        $blockFactory = Factory::getBlockFactory();
-        $element = $this->_rootElement->find(
-            sprintf($this->groupBlock, $name),
-            Locator::SELECTOR_XPATH
+        $tabUrl = $this->baseUrl . 'section/' . $tabName;
+        if ($this->getBrowserUrl() !== $tabUrl) {
+            $this->browser->open($tabUrl);
+        }
+        $this->waitForElementNotVisible($this->tabReadiness);
+
+        $groupElement = $this->_rootElement->find(
+            sprintf($this->groupBlock, $tabName, $groupName),
+            Locator::SELECTOR_CSS
         );
-        return $blockFactory->getMagentoBackendSystemConfigFormGroup($element);
+
+        if (!$groupElement->isVisible()) {
+            $this->_rootElement->find(
+                sprintf($this->groupBlockLink, $tabName, $groupName),
+                Locator::SELECTOR_CSS
+            )->click();
+
+            $this->waitForElementNotVisible($this->tabReadiness);
+
+            $groupElement = $this->_rootElement->find(
+                sprintf($this->groupBlock, $tabName, $groupName),
+                Locator::SELECTOR_CSS
+            );
+        }
+
+        $blockFactory = Factory::getBlockFactory();
+        return $blockFactory->getMagentoBackendSystemConfigFormGroup($groupElement);
+    }
+
+    /**
+     * Retrieve url associated with the form.
+     */
+    public function getBrowserUrl()
+    {
+        return $this->browser->getUrl();
     }
 
     /**
-     * Save store configuration
+     * Save store configuration.
      */
     public function save()
     {
-        $this->_rootElement->find($this->saveButton, Locator::SELECTOR_XPATH)->click();
+        $this->_rootElement->find($this->saveButton, Locator::SELECTOR_CSS)->click();
     }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form/Group.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form/Group.php
index 09123d09767bf838415192989cd1f5f06732360b..efaf58fed1584fc63f6b84fd79fe2bffceb7619f 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form/Group.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form/Group.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Store configuration group
+ * Store configuration group.
  *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
@@ -12,72 +12,62 @@ use Magento\Mtf\Client\Locator;
 use Magento\Mtf\Block\Form;
 
 /**
- * Class Group
+ * Class Group.
  */
 class Group extends Form
 {
     /**
-     * Fieldset selector
+     * Fieldset selector.
      *
      * @var string
      */
-    protected $fieldset = 'fieldset';
+    protected $fieldset = '#%s_%s';
 
     /**
-     * Toggle link
+     * Field selector.
      *
      * @var string
      */
-    protected $toogleLink = '.entry-edit-head a';
+    protected $field = '#%s_%s_%s';
 
     /**
-     * Field element selector
+     * Default checkbox selector.
      *
      * @var string
      */
-    protected $element = '//*[@data-ui-id="%s"]';
+    protected $defaultCheckbox = '#%s_%s_%s_inherit';
 
     /**
-     * Default checkbox selector
+     * Set store configuration value by element data-ui-id.
      *
-     * @var string
-     */
-    protected $defaultCheckbox = '//*[@data-ui-id="%s"]/../../*[@class="use-default"]/input';
-
-    /**
-     * Open group fieldset
-     */
-    public function open()
-    {
-        if (!$this->_rootElement->find($this->fieldset)->isVisible()) {
-            $this->_rootElement->find($this->toogleLink)->click();
-        }
-    }
-
-    /**
-     * Set store configuration value by element data-ui-id
-     *
-     * @param string $field
+     * @param string $tabName
+     * @param string $groupName
+     * @param string $fieldName
      * @param mixed $value
      */
-    public function setValue($field, $value)
+    public function setValue($tabName, $groupName, $fieldName, $value)
     {
         $input = null;
-        $fieldParts = explode('-', $field);
-        if (in_array($fieldParts[0], ['select', 'checkbox'])) {
-            $input = $fieldParts[0];
+        $attribute = $this->_rootElement->find(
+            sprintf($this->field, $tabName, $groupName, $fieldName),
+            Locator::SELECTOR_CSS
+        )->getAttribute('data-ui-id');
+
+        $parts = explode('-', $attribute, 2);
+        if (in_array($parts[0], ['select', 'text', 'checkbox'])) {
+            $input = $parts[0];
         }
 
         $element = $this->_rootElement->find(
-            sprintf($this->element, $field),
-            Locator::SELECTOR_XPATH,
+            sprintf($this->field, $tabName, $groupName, $fieldName),
+            Locator::SELECTOR_CSS,
             $input
         );
 
         if ($element->isDisabled()) {
             $checkbox = $this->_rootElement->find(
-                sprintf($this->defaultCheckbox, $field),
-                Locator::SELECTOR_XPATH,
+                sprintf($this->defaultCheckbox, $tabName, $groupName, $fieldName),
+                Locator::SELECTOR_CSS,
                 'checkbox'
             );
             $checkbox->setValue('No');
@@ -85,4 +75,20 @@ class Group extends Form
 
         $element->setValue($value);
     }
+
+    /**
+     * Check if a field is visible in a given group.
+     *
+     * @param string $tabName
+     * @param string $groupName
+     * @param string $fieldName
+     * @return bool
+     */
+    public function isFieldVisible($tabName, $groupName, $fieldName)
+    {
+        $this->_rootElement->find(
+            sprintf($this->field, $tabName, $groupName, $fieldName),
+            Locator::SELECTOR_CSS
+        )->isVisible();
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsAvailable.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsAvailable.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc670da5b445fe8713004605b92f62c5faac6f59
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsAvailable.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Backend\Test\Constraint;
+
+use Magento\Mtf\Constraint\AbstractConstraint;
+use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
+use Magento\Config\Test\Fixture\ConfigData;
+
+/**
+ * Assert that https header options are available.
+ */
+class AssertHttpsHeaderOptionsAvailable extends AbstractConstraint
+{
+    /**
+     * Assert that https header options are available.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $hsts
+     * @param ConfigData $upgradeInsecure
+     * @return void
+     */
+    public function processAssert(
+        SystemConfigEdit $systemConfigEdit,
+        ConfigData $hsts,
+        ConfigData $upgradeInsecure
+    ) {
+        $this->verifyConfiguration($systemConfigEdit, $hsts);
+        $this->verifyConfiguration($systemConfigEdit, $upgradeInsecure);
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'HTTPS headers configuration verification successfully.';
+    }
+
+    /**
+     * Verify configurations.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $config
+     * @return void
+     */
+    private function verifyConfiguration(SystemConfigEdit $systemConfigEdit, ConfigData $config)
+    {
+        $section = $config->getSection();
+        $keys = array_keys($section);
+        foreach ($keys as $key) {
+            $parts = explode('/', $key, 3);
+            $tabName = $parts[0];
+            $groupName = $parts[1];
+            $fieldName = $parts[2];
+            try {
+                $group = $systemConfigEdit->getForm()->getGroup($tabName, $groupName);
+                $group->setValue($tabName, $groupName, $fieldName, 'Yes');
+                $group->setValue($tabName, $groupName, $fieldName, 'No');
+                \PHPUnit_Framework_Assert::assertTrue(
+                    true,
+                    $fieldName . " configuration is enabled with options Yes & No."
+                );
+            } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
+                \PHPUnit_Framework_Assert::assertFalse(
+                    true,
+                    $fieldName . " configuration is not enabled with options Yes & No."
+                );
+            }
+        }
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsNotAvailable.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsNotAvailable.php
new file mode 100644
index 0000000000000000000000000000000000000000..e1c92159979006779b74d529fcb6e90e61138327
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsHeaderOptionsNotAvailable.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Backend\Test\Constraint;
+
+use Magento\Mtf\Constraint\AbstractConstraint;
+use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
+use Magento\Config\Test\Fixture\ConfigData;
+
+/**
+ * Assert that https header options are not available.
+ */
+class AssertHttpsHeaderOptionsNotAvailable extends AbstractConstraint
+{
+    /**
+     * Assert that https header options are available.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $hsts
+     * @param ConfigData $upgradeInsecure
+     * @return void
+     */
+    public function processAssert(
+        SystemConfigEdit $systemConfigEdit,
+        ConfigData $hsts,
+        ConfigData $upgradeInsecure
+    ) {
+        $this->verifyConfiguration($systemConfigEdit, $hsts);
+        $this->verifyConfiguration($systemConfigEdit, $upgradeInsecure);
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'HTTPS headers not visible verification successfully.';
+    }
+
+    /**
+     * Verify configurations.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $config
+     * @return void
+     */
+    private function verifyConfiguration(
+        SystemConfigEdit $systemConfigEdit,
+        ConfigData $config
+    ) {
+        $section = $config->getSection();
+        $keys = array_keys($section);
+        foreach ($keys as $key) {
+            $parts = explode('/', $key, 3);
+            $tabName = $parts[0];
+            $groupName = $parts[1];
+            $fieldName = $parts[2];
+            $isVisible = $systemConfigEdit->getForm()->getGroup($tabName, $groupName)
+                ->isFieldVisible($tabName, $groupName, $fieldName);
+            \PHPUnit_Framework_Assert::assertTrue(
+                !$isVisible,
+                $fieldName . " configuration is not visible."
+            );
+        }
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertStoreCanBeLocalized.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertStoreCanBeLocalized.php
index fd241121f62fc88eaa86debe9df1aa86242f53b0..97d98e8c332896edf8c74df3260c7bd2311c4c71 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertStoreCanBeLocalized.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertStoreCanBeLocalized.php
@@ -39,9 +39,8 @@ class AssertStoreCanBeLocalized extends AbstractConstraint
         $systemConfig->open();
         $systemConfig->getPageActions()->selectStore($store->getGroupId() . "/" . $store->getName());
         $systemConfig->getModalBlock()->acceptAlert();
-        $configGroup = $systemConfig->getForm()->getGroup('Locale Options');
-        $configGroup->open();
-        $configGroup->setValue('select-groups-locale-fields-code-value', $locale);
+        $configGroup = $systemConfig->getForm()->getGroup('general', 'locale', 'code');
+        $configGroup->setValue('general', 'locale', 'code', $locale);
         $systemConfig->getPageActions()->save();
         $systemConfig->getMessagesBlock()->waitSuccessMessage();
 
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/SystemConfigEdit.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/SystemConfigEdit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2d9ad23671175d8ce9d2d6092cafc783fc4425c4
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/SystemConfigEdit.xml
@@ -0,0 +1,15 @@
+<?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/pages.xsd">
+    <page name="SystemConfigEdit" area="Adminhtml" mca="admin/system_config/edit" module="Magento_Backend">
+        <block name="pageActions" class="Magento\Backend\Test\Block\System\Config\PageActions" locator=".page-main-actions" strategy="css selector"/>
+        <block name="form" class="Magento\Backend\Test\Block\System\Config\Form" locator="#config-edit-form" strategy="css selector"/>
+        <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages" strategy="css selector"/>
+        <block name="modalBlock" class="Magento\Ui\Test\Block\Adminhtml\Modal" locator="._show[data-role=modal]" strategy="css selector"/>
+    </page>
+</config>
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml
index 107555040bc1dd643b556b0bae34f3b4fe9cd9b7..9ac49e41ebb6f8781f980ce03e4d8d275a7402c8 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml
@@ -132,5 +132,50 @@
                 <item name="value" xsi:type="number">0</item>
             </field>
         </dataset>
+
+        <dataset name="enable_https_frontend_admin">
+            <field name="web/secure/use_in_frontend" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">Yes</item>
+                <item name="value" xsi:type="number">1</item>
+            </field>
+            <field name="web/secure/use_in_adminhtml" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">Yes</item>
+                <item name="value" xsi:type="number">1</item>
+            </field>
+        </dataset>
+        <dataset name="enable_hsts">
+            <field name="web/secure/enable_hsts" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">Yes</item>
+                <item name="value" xsi:type="number">1</item>
+            </field>
+        </dataset>
+        <dataset name="enable_upgrade_insecure">
+            <field name="web/secure/enable_upgrade_insecure" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">Yes</item>
+                <item name="value" xsi:type="number">1</item>
+            </field>
+        </dataset>
+        <dataset name="enable_https_frontend_only">
+            <field name="web/secure/use_in_frontend" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">Yes</item>
+                <item name="value" xsi:type="number">1</item>
+            </field>
+            <field name="web/secure/use_in_adminhtml" xsi:type="array">
+                <item name="scope" xsi:type="string">default</item>
+                <item name="scope_id" xsi:type="number">0</item>
+                <item name="label" xsi:type="string">No</item>
+                <item name="value" xsi:type="number">0</item>
+            </field>
+        </dataset>
     </repository>
 </config>
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a296699acb7e5c2b49b6030fbf577dacdff152f
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Backend\Test\TestCase;
+
+use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
+use Magento\Config\Test\Fixture\ConfigData;
+use Magento\Mtf\TestCase\Injectable;
+
+/**
+ * Steps:
+ *
+ * 1. Login to backend.
+ * 2. Go to Stores -> Configuration -> General -> Web.
+ * 3. Set "Use Secure URLs on Storefront" to Yes.
+ * 4. Set "Use Secure URLs in Admin" to No.
+ * 5. Perform asserts.
+ *
+ * @ZephyrId MAGETWO-46903
+ */
+class HttpsHeadersDisableTest extends Injectable
+{
+    /* tags */
+    const MVP = 'no';
+    const DOMAIN = 'PS';
+    /* end tags */
+
+    /**
+     * Open backend system config and set configuration values.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $httpsConfig
+     * @return void
+     */
+    public function test(SystemConfigEdit $systemConfigEdit, ConfigData $httpsConfig)
+    {
+        $systemConfigEdit->open();
+        $section = $httpsConfig->getSection();
+        $keys = array_keys($section);
+        foreach ($keys as $key) {
+            $parts = explode('/', $key, 3);
+            $tabName = $parts[0];
+            $groupName = $parts[1];
+            $fieldName = $parts[2];
+            $systemConfigEdit->getForm()->getGroup($tabName, $groupName)
+                ->setValue($tabName, $groupName, $fieldName, $section[$key]['label']);
+        }
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b043ed89adcc317dbde4aaef113d27f29e0b19d8
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersDisableTest.xml
@@ -0,0 +1,18 @@
+<?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\Backend\Test\TestCase\HttpsHeadersDisableTest" summary="HTTPS header options" ticketId="MAGETWO-46903">
+        <variation name="DisableHttpsHeaderOptions" summary="Disable HTTPS header options" ticketId="MAGETWO-46903">
+            <data name="httpsConfig/dataset" xsi:type="string">enable_https_frontend_only</data>
+            <data name="hsts/dataset" xsi:type="string">enable_hsts</data>
+            <data name="upgradeInsecure/dataset" xsi:type="string">enable_upgrade_insecure</data>
+            <constraint name="Magento\Backend\Test\Constraint\AssertHttpsHeaderOptionsNotAvailable"/>
+        </variation>
+    </testCase>
+</config>
+
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f6269a49a212f856b555b4cd44c1d11431ad0b4f
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Backend\Test\TestCase;
+
+use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
+use Magento\Config\Test\Fixture\ConfigData;
+use Magento\Mtf\TestCase\Injectable;
+
+/**
+ * Steps:
+ *
+ * 1. Login to backend.
+ * 2. Go to Stores -> Configuration -> General -> Web.
+ * 3. Set "Use Secure URLs on Storefront" to Yes.
+ * 4. Set "Use Secure URLs in Admin" to Yes.
+ * 5. Perform asserts.
+ *
+ * @ZephyrId MAGETWO-46903
+ */
+class HttpsHeadersEnableTest extends Injectable
+{
+    /* tags */
+    const MVP = 'no';
+    const DOMAIN = 'PS';
+    /* end tags */
+
+    /**
+     * Open backend system config and set configuration values.
+     *
+     * @param SystemConfigEdit $systemConfigEdit
+     * @param ConfigData $httpsConfig
+     * @return void
+     */
+    public function test(SystemConfigEdit $systemConfigEdit, ConfigData $httpsConfig)
+    {
+        $systemConfigEdit->open();
+        $section = $httpsConfig->getSection();
+        $keys = array_keys($section);
+        foreach ($keys as $key) {
+            $parts = explode('/', $key, 3);
+            $tabName = $parts[0];
+            $groupName = $parts[1];
+            $fieldName = $parts[2];
+            $systemConfigEdit->getForm()->getGroup($tabName, $groupName)
+                ->setValue($tabName, $groupName, $fieldName, $section[$key]['label']);
+        }
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..492aed203098bb41919310368fee37df8b38cd70
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/HttpsHeadersEnableTest.xml
@@ -0,0 +1,18 @@
+<?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\Backend\Test\TestCase\HttpsHeadersEnableTest" summary="HTTPS header options" ticketId="MAGETWO-46903">
+        <variation name="EnableHttpsHeaderOptions" summary="Enable HTTPS header options" ticketId="MAGETWO-46903">
+            <data name="httpsConfig/dataset" xsi:type="string">enable_https_frontend_admin</data>
+            <data name="hsts/dataset" xsi:type="string">enable_hsts</data>
+            <data name="upgradeInsecure/dataset" xsi:type="string">enable_upgrade_insecure</data>
+            <constraint name="Magento\Backend\Test\Constraint\AssertHttpsHeaderOptionsAvailable"/>
+        </variation>
+    </testCase>
+</config>
+
diff --git a/lib/internal/Credis/Client.php b/lib/internal/Credis/Client.php
deleted file mode 100644
index 5945709b837c8c54cf4c8116dee870f8481a0c46..0000000000000000000000000000000000000000
--- a/lib/internal/Credis/Client.php
+++ /dev/null
@@ -1,1113 +0,0 @@
-<?php
-/**
- * Credis_Client (a fork of Redisent)
- *
- * Most commands are compatible with phpredis library:
- *   - use "pipeline()" to start a pipeline of commands instead of multi(Redis::PIPELINE)
- *   - any arrays passed as arguments will be flattened automatically
- *   - setOption and getOption are not supported in standalone mode
- *   - order of arguments follows redis-cli instead of phpredis where they differ (lrem)
- *
- * - Uses phpredis library if extension is installed for better performance.
- * - Establishes connection lazily.
- * - Supports tcp and unix sockets.
- * - Reconnects automatically unless a watch or transaction is in progress.
- * - Can set automatic retry connection attempts for iffy Redis connections.
- *
- * @author Colin Mollenhour <colin@mollenhour.com>
- * @copyright 2011 Colin Mollenhour <colin@mollenhour.com>
- * @license http://www.opensource.org/licenses/mit-license.php The MIT License
- * @package Credis_Client
- */
-
-if( ! defined('CRLF')) define('CRLF', sprintf('%s%s', chr(13), chr(10)));
-
-/**
- * Credis-specific errors, wraps native Redis errors
- */
-class CredisException extends Exception
-{
-
-    const CODE_TIMED_OUT = 1;
-    const CODE_DISCONNECTED = 2;
-
-    public function __construct($message, $code = 0, $exception = NULL)
-    {
-        if ($exception && get_class($exception) == 'RedisException' && $message == 'read error on connection') {
-            $code = CredisException::CODE_DISCONNECTED;
-        }
-        parent::__construct($message, $code, $exception);
-    }
-
-}
-
-/**
- * Credis_Client, a lightweight Redis PHP standalone client and phpredis wrapper
- *
- * Server/Connection:
- * @method Credis_Client pipeline()
- * @method Credis_Client multi()
- * @method array         exec()
- * @method string        flushAll()
- * @method string        flushDb()
- * @method array         info()
- * @method bool|array    config(string $setGet, string $key, string $value = null)
- *
- * Keys:
- * @method int           del(string $key)
- * @method int           exists(string $key)
- * @method int           expire(string $key, int $seconds)
- * @method int           expireAt(string $key, int $timestamp)
- * @method array         keys(string $key)
- * @method int           persist(string $key)
- * @method bool          rename(string $key, string $newKey)
- * @method bool          renameNx(string $key, string $newKey)
- * @method array         sort(string $key, string $arg1, string $valueN = null)
- * @method int           ttl(string $key)
- * @method string        type(string $key)
- *
- * Scalars:
- * @method int           append(string $key, string $value)
- * @method int           decr(string $key)
- * @method int           decrBy(string $key, int $decrement)
- * @method bool|string   get(string $key)
- * @method int           getBit(string $key, int $offset)
- * @method string        getRange(string $key, int $start, int $end)
- * @method string        getSet(string $key, string $value)
- * @method int           incr(string $key)
- * @method int           incrBy(string $key, int $decrement)
- * @method array         mGet(array $keys)
- * @method bool          mSet(array $keysValues)
- * @method int           mSetNx(array $keysValues)
- * @method bool          set(string $key, string $value)
- * @method int           setBit(string $key, int $offset, int $value)
- * @method bool          setEx(string $key, int $seconds, string $value)
- * @method int           setNx(string $key, string $value)
- * @method int           setRange(string $key, int $offset, int $value)
- * @method int           strLen(string $key)
- *
- * Sets:
- * @method int           sAdd(string $key, mixed $value, string $valueN = null)
- * @method int           sRem(string $key, mixed $value, string $valueN = null)
- * @method array         sMembers(string $key)
- * @method array         sUnion(mixed $keyOrArray, string $valueN = null)
- * @method array         sInter(mixed $keyOrArray, string $valueN = null)
- * @method array         sDiff(mixed $keyOrArray, string $valueN = null)
- * @method string        sPop(string $key)
- * @method int           sCard(string $key)
- * @method int           sIsMember(string $key, string $member)
- * @method int           sMove(string $source, string $dest, string $member)
- * @method string|array  sRandMember(string $key, int $count = null)
- * @method int           sUnionStore(string $dest, string $key1, string $key2 = null)
- * @method int           sInterStore(string $dest, string $key1, string $key2 = null)
- * @method int           sDiffStore(string $dest, string $key1, string $key2 = null)
- *
- * Hashes:
- * @method bool|int      hSet(string $key, string $field, string $value)
- * @method bool          hSetNx(string $key, string $field, string $value)
- * @method bool|string   hGet(string $key, string $field)
- * @method bool|int      hLen(string $key)
- * @method bool          hDel(string $key, string $field)
- * @method array         hKeys(string $key, string $field)
- * @method array         hVals(string $key, string $field)
- * @method array         hGetAll(string $key)
- * @method bool          hExists(string $key, string $field)
- * @method int           hIncrBy(string $key, string $field, int $value)
- * @method bool          hMSet(string $key, array $keysValues)
- * @method array         hMGet(string $key, array $fields)
- *
- * Lists:
- * @method array|null    blPop(string $keyN, int $timeout)
- * @method array|null    brPop(string $keyN, int $timeout)
- * @method array|null    brPoplPush(string $source, string $destination, int $timeout)
- * @method string|null   lIndex(string $key, int $index)
- * @method int           lInsert(string $key, string $beforeAfter, string $pivot, string $value)
- * @method int           lLen(string $key)
- * @method string|null   lPop(string $key)
- * @method int           lPush(string $key, mixed $value, mixed $valueN = null)
- * @method int           lPushX(string $key, mixed $value)
- * @method array         lRange(string $key, int $start, int $stop)
- * @method int           lRem(string $key, int $count, mixed $value)
- * @method bool          lSet(string $key, int $index, mixed $value)
- * @method bool          lTrim(string $key, int $start, int $stop)
- * @method string|null   rPop(string $key)
- * @method string|null   rPoplPush(string $source, string $destination)
- * @method int           rPush(string $key, mixed $value, mixed $valueN = null)
- * @method int           rPushX(string $key, mixed $value)
- *
- * Sorted Sets:
- * TODO
- *
- * Pub/Sub
- * @method array         pUnsubscribe(mixed $pattern, string $patternN = NULL))
- * @method array         unsubscribe(mixed $channel, string $channelN = NULL))
- * @method int           publish(string $channel, string $message)
- * @method int|array     pubsub(string $subCommand, $arg = NULL)
- *
- * Scripting:
- * @method string|int    script(string $command, string $arg1 = null)
- * @method string|int|array|bool eval(string $script, array $keys = NULL, array $args = NULL)
- * @method string|int|array|bool evalSha(string $script, array $keys = NULL, array $args = NULL)
- */
-class Credis_Client {
-
-    const TYPE_STRING      = 'string';
-    const TYPE_LIST        = 'list';
-    const TYPE_SET         = 'set';
-    const TYPE_ZSET        = 'zset';
-    const TYPE_HASH        = 'hash';
-    const TYPE_NONE        = 'none';
-    const FREAD_BLOCK_SIZE = 8192;
-
-    /**
-     * Socket connection to the Redis server or Redis library instance
-     * @var resource|Redis
-     */
-    protected $redis;
-    protected $redisMulti;
-
-    /**
-     * Host of the Redis server
-     * @var string
-     */
-    protected $host;
-    
-    /**
-     * Port on which the Redis server is running
-     * @var integer
-     */
-    protected $port;
-
-    /**
-     * Timeout for connecting to Redis server
-     * @var float
-     */
-    protected $timeout;
-
-    /**
-     * Timeout for reading response from Redis server
-     * @var float
-     */
-    protected $readTimeout;
-
-    /**
-     * Unique identifier for persistent connections
-     * @var string
-     */
-    protected $persistent;
-
-    /**
-     * @var bool
-     */
-    protected $closeOnDestruct = TRUE;
-
-    /**
-     * @var bool
-     */
-    protected $connected = FALSE;
-
-    /**
-     * @var bool
-     */
-    protected $standalone;
-
-    /**
-     * @var int
-     */
-    protected $maxConnectRetries = 0;
-
-    /**
-     * @var int
-     */
-    protected $connectFailures = 0;
-
-    /**
-     * @var bool
-     */
-    protected $usePipeline = FALSE;
-
-    /**
-     * @var array
-     */
-    protected $commandNames;
-
-    /**
-     * @var string
-     */
-    protected $commands;
-
-    /**
-     * @var bool
-     */
-    protected $isMulti = FALSE;
-
-    /**
-     * @var bool
-     */
-    protected $isWatching = FALSE;
-
-    /**
-     * @var string
-     */
-    protected $authPassword;
-
-    /**
-     * @var int
-     */
-    protected $selectedDb = 0;
-
-    /**
-     * Aliases for backwards compatibility with phpredis
-     * @var array
-     */
-    protected $wrapperMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem');
-
-    /**
-     * @var array
-     */
-    protected $renamedCommands;
-
-    /**
-     * @var int
-     */
-    protected $requests = 0;
-
-    /**
-     * Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
-     * $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path]
-     *
-     * @param string $host The hostname of the Redis server
-     * @param integer $port The port number of the Redis server
-     * @param float $timeout  Timeout period in seconds
-     * @param string $persistent  Flag to establish persistent connection
-     * @param int $db The selected datbase of the Redis server
-     * @param string $password The authentication password of the Redis server
-     */
-    public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null)
-    {
-        $this->host = (string) $host;
-        $this->port = (int) $port;
-        $this->timeout = $timeout;
-        $this->persistent = (string) $persistent;
-        $this->standalone = ! extension_loaded('redis');
-        $this->authPassword = $password;
-        $this->selectedDb = (int)$db;
-        $this->convertHost();
-    }
-
-    public function __destruct()
-    {
-        if ($this->closeOnDestruct) {
-            $this->close();
-        }
-    }
-    /**
-     * Return the host of the Redis instance
-     * @return string
-     */
-    public function getHost()
-    {
-        return $this->host;
-    }
-    /**
-     * Return the port of the Redis instance
-     * @return int
-     */
-    public function getPort()
-    {
-        return $this->port;
-    }
-
-    /**
-     * Return the selected database
-     * @return int
-     */
-    public function getSelectedDb()
-    {
-        return $this->selectedDb;
-    }
-    /**
-     * @return string
-     */
-    public function getPersistence()
-    {
-        return $this->persistent;
-    }
-    /**
-     * @throws CredisException
-     * @return Credis_Client
-     */
-    public function forceStandalone()
-    {
-        if($this->connected) {
-            throw new CredisException('Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.');
-        }
-        $this->standalone = TRUE;
-        return $this;
-    }
-
-    /**
-     * @param int $retries
-     * @return Credis_Client
-     */
-    public function setMaxConnectRetries($retries)
-    {
-        $this->maxConnectRetries = $retries;
-        return $this;
-    }
-
-    /**
-     * @param bool $flag
-     * @return Credis_Client
-     */
-    public function setCloseOnDestruct($flag)
-    {
-        $this->closeOnDestruct = $flag;
-        return $this;
-    }
-    protected function convertHost()
-    {
-        if (preg_match('#^(tcp|unix)://(.*)$#', $this->host, $matches)) {
-            if($matches[1] == 'tcp') {
-                if ( ! preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) {
-                    throw new CredisException('Invalid host format; expected tcp://host[:port][/persistence_identifier]');
-                }
-                $this->host = $matches[1];
-                $this->port = (int) (isset($matches[3]) ? $matches[3] : 6379);
-                $this->persistent = isset($matches[5]) ? $matches[5] : '';
-            } else {
-                $this->host = $matches[2];
-                $this->port = NULL;
-                if (substr($this->host,0,1) != '/') {
-                    throw new CredisException('Invalid unix socket format; expected unix:///path/to/redis.sock');
-                }
-            }
-        }
-        if ($this->port !== NULL && substr($this->host,0,1) == '/') {
-            $this->port = NULL;
-        }
-    }
-    /**
-     * @throws CredisException
-     * @return Credis_Client
-     */
-    public function connect()
-    {
-        if ($this->connected) {
-            return $this;
-        }
-        if ($this->standalone) {
-            $flags = STREAM_CLIENT_CONNECT;
-            $remote_socket = $this->port === NULL
-                ? 'unix://'.$this->host
-                : 'tcp://'.$this->host.':'.$this->port;
-            if ($this->persistent) {
-                if ($this->port === NULL) { // Unix socket
-                    throw new CredisException('Persistent connections to UNIX sockets are not supported in standalone mode.');
-                }
-                $remote_socket .= '/'.$this->persistent;
-                $flags = $flags | STREAM_CLIENT_PERSISTENT;
-            }
-            $result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags);
-        }
-        else {
-            if ( ! $this->redis) {
-                $this->redis = new Redis;
-            }
-            $result = $this->persistent
-                ? $this->redis->pconnect($this->host, $this->port, $this->timeout, $this->persistent)
-                : $this->redis->connect($this->host, $this->port, $this->timeout);
-        }
-
-        // Use recursion for connection retries
-        if ( ! $result) {
-            $this->connectFailures++;
-            if ($this->connectFailures <= $this->maxConnectRetries) {
-                return $this->connect();
-            }
-            $failures = $this->connectFailures;
-            $this->connectFailures = 0;
-            throw new CredisException("Connection to Redis failed after $failures failures." . (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : ""));
-        }
-
-        $this->connectFailures = 0;
-        $this->connected = TRUE;
-
-        // Set read timeout
-        if ($this->readTimeout) {
-            $this->setReadTimeout($this->readTimeout);
-        }
-
-        if($this->authPassword !== null) {
-            $this->auth($this->authPassword);
-        }
-        if($this->selectedDb !== 0) {
-            $this->select($this->selectedDb);
-        }
-        return $this;
-    }
-    /**
-     * @return bool
-     */
-    public function isConnected()
-    {
-        return $this->connected;
-    }
-    /**
-     * Set the read timeout for the connection. Use 0 to disable timeouts entirely (or use a very long timeout
-     * if not supported).
-     *
-     * @param int $timeout 0 (or -1) for no timeout, otherwise number of seconds
-     * @throws CredisException
-     * @return Credis_Client
-     */
-    public function setReadTimeout($timeout)
-    {
-        if ($timeout < -1) {
-            throw new CredisException('Timeout values less than -1 are not accepted.');
-        }
-        $this->readTimeout = $timeout;
-        if ($this->connected) {
-            if ($this->standalone) {
-                $timeout = $timeout <= 0 ? 315360000 : $timeout; // Ten-year timeout
-                stream_set_blocking($this->redis, TRUE);
-                stream_set_timeout($this->redis, (int) floor($timeout), ($timeout - floor($timeout)) * 1000000);
-            } else if (defined('Redis::OPT_READ_TIMEOUT')) {
-                // supported in phpredis 2.2.3
-                // a timeout value of -1 means reads will not timeout
-                $timeout = $timeout == 0 ? -1 : $timeout;
-                $this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout);
-            }
-        }
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function close()
-    {
-        $result = TRUE;
-        if ($this->connected && ! $this->persistent) {
-            try {
-                $result = $this->standalone ? fclose($this->redis) : $this->redis->close();
-                $this->connected = FALSE;
-            } catch (Exception $e) {
-                ; // Ignore exceptions on close
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * Enabled command renaming and provide mapping method. Supported methods are:
-     *
-     * 1. renameCommand('foo') // Salted md5 hash for all commands -> md5('foo'.$command)
-     * 2. renameCommand(function($command){ return 'my'.$command; }); // Callable
-     * 3. renameCommand('get', 'foo') // Single command -> alias
-     * 4. renameCommand(['get' => 'foo', 'set' => 'bar']) // Full map of [command -> alias]
-     *
-     * @param string|callable|array $command
-     * @param string|null $alias
-     * @return $this
-     */
-    public function renameCommand($command, $alias = NULL)
-    {
-        if ( ! $this->standalone) {
-            $this->forceStandalone();
-        }
-        if ($alias === NULL) {
-            $this->renamedCommands = $command;
-        } else {
-            if ( ! $this->renamedCommands) {
-                $this->renamedCommands = array();
-            }
-            $this->renamedCommands[$command] = $alias;
-        }
-        return $this;
-    }
-
-    /**
-     * @param $command
-     */
-    public function getRenamedCommand($command)
-    {
-        static $map;
-
-        // Command renaming not enabled
-        if ($this->renamedCommands === NULL) {
-            return $command;
-        }
-
-        // Initialize command map
-        if ($map === NULL) {
-            if (is_array($this->renamedCommands)) {
-                $map = $this->renamedCommands;
-            } else {
-                $map = array();
-            }
-        }
-
-        // Generate and return cached result
-        if ( ! isset($map[$command])) {
-            // String means all commands are hashed with salted md5
-            if (is_string($this->renamedCommands)) {
-                $map[$command] = md5($this->renamedCommands.$command);
-            }
-            // Would already be set in $map if it was intended to be renamed
-            else if (is_array($this->renamedCommands)) {
-                return $command;
-            }
-            // User-supplied function
-            else if (is_callable($this->renamedCommands)) {
-                $map[$command] = call_user_func($this->renamedCommands, $command);
-            }
-        }
-        return $map[$command];
-    }
-
-    /**
-     * @param string $password
-     * @return bool
-     */
-    public function auth($password)
-    {
-        $response = $this->__call('auth', array($password));
-        $this->authPassword = $password;
-        return $response;
-    }
-
-    /**
-     * @param int $index
-     * @return bool
-     */
-    public function select($index)
-    {
-        $response = $this->__call('select', array($index));
-        $this->selectedDb = (int) $index;
-        return $response;
-    }
-
-    /**
-     * @param string|array $patterns
-     * @param $callback
-     * @return $this|array|bool|Credis_Client|mixed|null|string
-     * @throws CredisException
-     */
-    public function pSubscribe($patterns, $callback)
-    {
-        if ( ! $this->standalone) {
-            return $this->__call('pSubscribe', array((array)$patterns, $callback));
-        }
-
-        // Standalone mode: use infinite loop to subscribe until timeout
-        $patternCount = is_array($patterns) ? count($patterns) : 1;
-        while ($patternCount--) {
-            if (isset($status)) {
-                list($command, $pattern, $status) = $this->read_reply();
-            } else {
-                list($command, $pattern, $status) = $this->__call('psubscribe', array($patterns));
-            }
-            if ( ! $status) {
-                throw new CredisException('Invalid pSubscribe response.');
-            }
-        }
-        try {
-            while (1) {
-                list($type, $pattern, $channel, $message) = $this->read_reply();
-                if ($type != 'pmessage') {
-                    throw new CredisException('Received non-pmessage reply.');
-                }
-                $callback($this, $pattern, $channel, $message);
-            }
-        } catch (CredisException $e) {
-            if ($e->getCode() == CredisException::CODE_TIMED_OUT) {
-                try {
-                    list($command, $pattern, $status) = $this->pUnsubscribe($patterns);
-                    while ($status !== 0) {
-                        list($command, $pattern, $status) = $this->read_reply();
-                    }
-                } catch (CredisException $e2) {
-                    throw $e2;
-                }
-            }
-            throw $e;
-        }
-    }
-
-    /**
-     * @param string|array $channels
-     * @param $callback
-     * @throws CredisException
-     * @return $this|array|bool|Credis_Client|mixed|null|string
-     */
-    public function subscribe($channels, $callback)
-    {
-        if ( ! $this->standalone) {
-            return $this->__call('subscribe', array((array)$channels, $callback));
-        }
-
-        // Standalone mode: use infinite loop to subscribe until timeout
-        $channelCount = is_array($channels) ? count($channels) : 1;
-        while ($channelCount--) {
-            if (isset($status)) {
-                list($command, $channel, $status) = $this->read_reply();
-            } else {
-                list($command, $channel, $status) = $this->__call('subscribe', array($channels));
-            }
-            if ( ! $status) {
-                throw new CredisException('Invalid subscribe response.');
-            }
-        }
-        try {
-            while (1) {
-                list($type, $channel, $message) = $this->read_reply();
-                if ($type != 'message') {
-                    throw new CredisException('Received non-message reply.');
-                }
-                $callback($this, $channel, $message);
-            }
-        } catch (CredisException $e) {
-            if ($e->getCode() == CredisException::CODE_TIMED_OUT) {
-                try {
-                    list($command, $channel, $status) = $this->unsubscribe($channels);
-                    while ($status !== 0) {
-                        list($command, $channel, $status) = $this->read_reply();
-                    }
-                } catch (CredisException $e2) {
-                    throw $e2;
-                }
-            }
-            throw $e;
-        }
-    }
-
-    public function __call($name, $args)
-    {
-        // Lazy connection
-        $this->connect();
-
-        $name = strtolower($name);
-
-        // Send request via native PHP
-        if($this->standalone)
-        {
-            switch ($name) {
-                case 'eval':
-                case 'evalsha':
-                    $script = array_shift($args);
-                    $keys = (array) array_shift($args);
-                    $eArgs = (array) array_shift($args);
-                    $args = array($script, count($keys), $keys, $eArgs);
-                    break;
-            }
-            // Flatten arguments
-            $argsFlat = NULL;
-            foreach($args as $index => $arg) {
-                if(is_array($arg)) {
-                    if($argsFlat === NULL) {
-                        $argsFlat = array_slice($args, 0, $index);
-                    }
-                    if($name == 'mset' || $name == 'msetnx' || $name == 'hmset') {
-                      foreach($arg as $key => $value) {
-                        $argsFlat[] = $key;
-                        $argsFlat[] = $value;
-                      }
-                    } else {
-                      $argsFlat = array_merge($argsFlat, $arg);
-                    }
-                } else if($argsFlat !== NULL) {
-                    $argsFlat[] = $arg;
-                }
-            }
-            if($argsFlat !== NULL) {
-                $args = $argsFlat;
-                $argsFlat = NULL;
-            }
-
-            // In pipeline mode
-            if($this->usePipeline)
-            {
-                if($name == 'pipeline') {
-                    throw new CredisException('A pipeline is already in use and only one pipeline is supported.');
-                }
-                else if($name == 'exec') {
-                    if($this->isMulti) {
-                        $this->commandNames[] = $name;
-                        $this->commands .= self::_prepare_command(array($this->getRenamedCommand($name)));
-                    }
-
-                    // Write request
-                    if($this->commands) {
-                        $this->write_command($this->commands);
-                    }
-                    $this->commands = NULL;
-
-                    // Read response
-                    $response = array();
-                    foreach($this->commandNames as $command) {
-                        $response[] = $this->read_reply($command);
-                    }
-                    $this->commandNames = NULL;
-
-                    if($this->isMulti) {
-                        $response = array_pop($response);
-                    }
-                    $this->usePipeline = $this->isMulti = FALSE;
-                    return $response;
-                }
-                else {
-                    if($name == 'multi') {
-                        $this->isMulti = TRUE;
-                    }
-                    array_unshift($args, $this->getRenamedCommand($name));
-                    $this->commandNames[] = $name;
-                    $this->commands .= self::_prepare_command($args);
-                    return $this;
-                }
-            }
-
-            // Start pipeline mode
-            if($name == 'pipeline')
-            {
-                $this->usePipeline = TRUE;
-                $this->commandNames = array();
-                $this->commands = '';
-                return $this;
-            }
-
-            // If unwatching, allow reconnect with no error thrown
-            if($name == 'unwatch') {
-                $this->isWatching = FALSE;
-            }
-
-            // Non-pipeline mode
-            array_unshift($args, $this->getRenamedCommand($name));
-            $command = self::_prepare_command($args);
-            $this->write_command($command);
-            $response = $this->read_reply($name);
-
-            // Watch mode disables reconnect so error is thrown
-            if($name == 'watch') {
-                $this->isWatching = TRUE;
-            }
-            // Transaction mode
-            else if($this->isMulti && ($name == 'exec' || $name == 'discard')) {
-                $this->isMulti = FALSE;
-            }
-            // Started transaction
-            else if($this->isMulti || $name == 'multi') {
-                $this->isMulti = TRUE;
-                $response = $this;
-            }
-        }
-
-        // Send request via phpredis client
-        else
-        {
-            // Tweak arguments
-            switch($name) {
-                case 'get':   // optimize common cases
-                case 'set':
-                case 'hget':
-                case 'hset':
-                case 'setex':
-                case 'mset':
-                case 'msetnx':
-                case 'hmset':
-                case 'hmget':
-                case 'del':
-                    break;
-                case 'mget':
-                    if(isset($args[0]) && ! is_array($args[0])) {
-                        $args = array($args);
-                    }
-                    break;
-                case 'lrem':
-                    $args = array($args[0], $args[2], $args[1]);
-                    break;
-                case 'eval':
-                case 'evalsha':
-                    if (isset($args[1]) && is_array($args[1])) {
-                        $cKeys = $args[1];
-                    } elseif (isset($args[1]) && is_string($args[1])) {
-                        $cKeys = array($args[1]);
-                    } else {
-                        $cKeys = array();
-                    }
-                    if (isset($args[2]) && is_array($args[2])) {
-                        $cArgs = $args[2];
-                    } elseif (isset($args[2]) && is_string($args[2])) {
-                        $cArgs = array($args[2]);
-                    } else {
-                        $cArgs = array();
-                    }
-                    $args = array($args[0], array_merge($cKeys, $cArgs), count($cKeys));
-                    break;
-                case 'subscribe':
-                case 'psubscribe':
-                    break;
-                default:
-                    // Flatten arguments
-                    $argsFlat = NULL;
-                    foreach($args as $index => $arg) {
-                        if(is_array($arg)) {
-                            if($argsFlat === NULL) {
-                                $argsFlat = array_slice($args, 0, $index);
-                            }
-                            $argsFlat = array_merge($argsFlat, $arg);
-                        } else if($argsFlat !== NULL) {
-                            $argsFlat[] = $arg;
-                        }
-                    }
-                    if($argsFlat !== NULL) {
-                        $args = $argsFlat;
-                        $argsFlat = NULL;
-                    }
-            }
-
-            try {
-                // Proxy pipeline mode to the phpredis library
-                if($name == 'pipeline' || $name == 'multi') {
-                    if($this->isMulti) {
-                        return $this;
-                    } else {
-                        $this->isMulti = TRUE;
-                        $this->redisMulti = call_user_func_array(array($this->redis, $name), $args);
-                    }
-                }
-                else if($name == 'exec' || $name == 'discard') {
-                    $this->isMulti = FALSE;
-                    $response = $this->redisMulti->$name();
-                    $this->redisMulti = NULL;
-                    #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
-                    return $response;
-                }
-
-                // Use aliases to be compatible with phpredis wrapper
-                if(isset($this->wrapperMethods[$name])) {
-                    $name = $this->wrapperMethods[$name];
-                }
-
-                // Multi and pipeline return self for chaining
-                if($this->isMulti) {
-                    call_user_func_array(array($this->redisMulti, $name), $args);
-                    return $this;
-                }
-
-                // Send request, retry one time when using persistent connections on the first request only
-                $this->requests++;
-                try {
-                    $response = call_user_func_array(array($this->redis, $name), $args);
-                } catch (RedisException $e) {
-                    if ($this->persistent && $this->requests == 1 && $e->getMessage() == 'read error on connection') {
-                        $this->connected = FALSE;
-                        $this->connect();
-                        $response = call_user_func_array(array($this->redis, $name), $args);
-                    } else {
-                        throw $e;
-                    }
-                }
-            }
-            // Wrap exceptions
-            catch(RedisException $e) {
-                $code = 0;
-                if ( ! ($result = $this->redis->IsConnected())) {
-                    $this->connected = FALSE;
-                    $code = CredisException::CODE_DISCONNECTED;
-                }
-                throw new CredisException($e->getMessage(), $code, $e);
-            }
-
-            #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
-
-            // change return values where it is too difficult to minim in standalone mode
-            switch($name)
-            {
-                case 'hmget':
-                    $response = array_values($response);
-                    break;
-
-                case 'type':
-                    $typeMap = array(
-                      self::TYPE_NONE,
-                      self::TYPE_STRING,
-                      self::TYPE_SET,
-                      self::TYPE_LIST,
-                      self::TYPE_ZSET,
-                      self::TYPE_HASH,
-                    );
-                    $response = $typeMap[$response];
-                    break;
-
-                // Handle scripting errors
-                case 'eval':
-                case 'evalsha':
-                case 'script':
-                    $error = $this->redis->getLastError();
-                    $this->redis->clearLastError();
-                    if ($error && substr($error,0,8) == 'NOSCRIPT') {
-                        $response = NULL;
-                    } else if ($error) {
-                        throw new CredisException($error);
-                    }
-                    break;
-                default:
-                    $error = $this->redis->getLastError();
-                    $this->redis->clearLastError();
-                    if ($error) {
-                        throw new CredisException($error);
-                    }
-                    break;
-            }
-        }
-
-        return $response;
-    }
-
-    protected function write_command($command)
-    {
-        // Reconnect on lost connection (Redis server "timeout" exceeded since last command)
-        if(feof($this->redis)) {
-            $this->close();
-            // If a watch or transaction was in progress and connection was lost, throw error rather than reconnect
-            // since transaction/watch state will be lost.
-            if(($this->isMulti && ! $this->usePipeline) || $this->isWatching) {
-                $this->isMulti = $this->isWatching = FALSE;
-                throw new CredisException('Lost connection to Redis server during watch or transaction.');
-            }
-            $this->connected = FALSE;
-            $this->connect();
-            if($this->authPassword) {
-                $this->auth($this->authPassword);
-            }
-            if($this->selectedDb != 0) {
-                $this->select($this->selectedDb);
-            }
-        }
-
-        $commandLen = strlen($command);
-        for ($written = 0; $written < $commandLen; $written += $fwrite) {
-            $fwrite = fwrite($this->redis, substr($command, $written));
-            if ($fwrite === FALSE || $fwrite == 0 ) {
-                $this->connected = FALSE;
-                throw new CredisException('Failed to write entire command to stream');
-            }
-        }
-    }
-
-    protected function read_reply($name = '')
-    {
-        $reply = fgets($this->redis);
-        if($reply === FALSE) {
-            $info = stream_get_meta_data($this->redis);
-            if ($info['timed_out']) {
-                throw new CredisException('Read operation timed out.', CredisException::CODE_TIMED_OUT);
-            } else {
-                $this->connected = FALSE;
-                throw new CredisException('Lost connection to Redis server.', CredisException::CODE_DISCONNECTED);
-            }
-        }
-        $reply = rtrim($reply, CRLF);
-        #echo "> $name: $reply\n";
-        $replyType = substr($reply, 0, 1);
-        switch ($replyType) {
-            /* Error reply */
-            case '-':
-                if($this->isMulti || $this->usePipeline) {
-                    $response = FALSE;
-                } else if ($name == 'evalsha' && substr($reply,0,9) == '-NOSCRIPT') {
-                    $response = NULL;
-                } else {
-                    throw new CredisException(substr($reply,0,4) == '-ERR' ? substr($reply, 5) : substr($reply,1));
-                }
-                break;
-            /* Inline reply */
-            case '+':
-                $response = substr($reply, 1);
-                if($response == 'OK' || $response == 'QUEUED') {
-                  return TRUE;
-                }
-                break;
-            /* Bulk reply */
-            case '$':
-                if ($reply == '$-1') return FALSE;
-                $size = (int) substr($reply, 1);
-                $response = stream_get_contents($this->redis, $size + 2);
-                if( ! $response) {
-                    $this->connected = FALSE;
-                    throw new CredisException('Error reading reply.');
-                }
-                $response = substr($response, 0, $size);
-                break;
-            /* Multi-bulk reply */
-            case '*':
-                $count = substr($reply, 1);
-                if ($count == '-1') return FALSE;
-
-                $response = array();
-                for ($i = 0; $i < $count; $i++) {
-                        $response[] = $this->read_reply();
-                }
-                break;
-            /* Integer reply */
-            case ':':
-                $response = intval(substr($reply, 1));
-                break;
-            default:
-                throw new CredisException('Invalid response: '.print_r($reply, TRUE));
-                break;
-        }
-
-        // Smooth over differences between phpredis and standalone response
-        switch($name)
-        {
-            case '': // Minor optimization for multi-bulk replies
-                break;
-            case 'config':
-            case 'hgetall':
-                $keys = $values = array();
-                while($response) {
-                    $keys[] = array_shift($response);
-                    $values[] = array_shift($response);
-                }
-                $response = count($keys) ? array_combine($keys, $values) : array();
-                break;
-            case 'info':
-                $lines = explode(CRLF, trim($response,CRLF));
-                $response = array();
-                foreach($lines as $line) {
-                    if ( ! $line || substr($line, 0, 1) == '#') {
-                      continue;
-                    }
-                    list($key, $value) = explode(':', $line, 2);
-                    $response[$key] = $value;
-                }
-                break;
-            case 'ttl':
-                if($response === -1) {
-                    $response = FALSE;
-                }
-                break;
-        }
-
-        return $response;
-    }
-
-    /**
-     * Build the Redis unified protocol command
-     *
-     * @param array $args
-     * @return string
-     */
-    private static function _prepare_command($args)
-    {
-        return sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array('self', '_map'), $args), CRLF), CRLF);
-    }
-
-    private static function _map($arg)
-    {
-        return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
-    }
-
-}
diff --git a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php
index c49d92ee281ce439e2cd948ed3df606624f9a525..4fbeaecc85d18ad7c22f386ecfd00d838d5b904d 100644
--- a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php
+++ b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php
@@ -56,6 +56,7 @@ class ConfigOptionsListConstants
      */
     const SESSION_SAVE_FILES = 'files';
     const SESSION_SAVE_DB = 'db';
+    const SESSION_SAVE_REDIS = 'redis';
     /**#@-*/
 
     /**
diff --git a/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php b/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php
index 9866775d1fecdd1d381a3058483cba1d828342dd..10b388638222c15e760a14a9b56722da1cf378ce 100644
--- a/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php
+++ b/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php
@@ -65,7 +65,7 @@ class SelectRenderer implements RendererInterface
      */
     public function render(Select $select, $sql = '')
     {
-        $sql = Select::SQL_SELECT . ' ';
+        $sql = Select::SQL_SELECT;
         foreach ($this->renderers as $renderer) {
             if (in_array($renderer['part'], [Select::COLUMNS, Select::FROM]) || $select->getPart($renderer['part'])) {
                 $sql = $renderer['renderer']->render($select, $sql);
diff --git a/lib/internal/Magento/Framework/Session/Config.php b/lib/internal/Magento/Framework/Session/Config.php
index f006da9dcf7b376505f64d7c8485997fe925ba1e..793794b61826e09f088fef985593e40aae9aada5 100644
--- a/lib/internal/Magento/Framework/Session/Config.php
+++ b/lib/internal/Magento/Framework/Session/Config.php
@@ -15,8 +15,6 @@ use Magento\Framework\Session\SaveHandlerInterface;
 
 /**
  * Magento session configuration
- *
- * @method Config setSaveHandler()
  */
 class Config implements ConfigInterface
 {
@@ -99,6 +97,11 @@ class Config implements ConfigInterface
      */
     protected $_scopeType;
 
+    /**
+     * @var string
+     */
+    private $saveHandlerName;
+
     /** @var  \Magento\Framework\ValidatorFactory */
     protected $_validatorFactory;
 
@@ -141,7 +144,6 @@ class Config implements ConfigInterface
             self::PARAM_SESSION_SAVE_METHOD,
             $defaultSaveHandler
         );
-        $saveMethod = $saveMethod === 'db' ? 'user' : $saveMethod;
         $this->setSaveHandler($saveMethod);
 
         /**
@@ -292,6 +294,38 @@ class Config implements ConfigInterface
         return (string)$this->getOption('session.name');
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setSaveHandler($saveHandler)
+    {
+        $this->setSaveHandlerName($saveHandler);
+        if ($saveHandler === 'db' || $saveHandler === 'redis') {
+            $saveHandler = 'user';
+        }
+        $this->setOption('session.save_handler', $saveHandler);
+        return $this;
+    }
+
+    /**
+     * Set save handler name
+     *
+     * @param string $saveHandlerName
+     * @return void
+     */
+    private function setSaveHandlerName($saveHandlerName)
+    {
+        $this->saveHandlerName = $saveHandlerName;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSaveHandlerName()
+    {
+        return $this->saveHandlerName;
+    }
+
     /**
      * Set session.save_path
      *
diff --git a/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php b/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php
index 6c2372d20be4fc7c759618cdda8a93dfad19fc21..8182a7638efd358333bc6ee23b0a95e58b32b605 100644
--- a/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php
+++ b/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php
@@ -170,4 +170,19 @@ interface ConfigInterface
      * @SuppressWarnings(PHPMD.BooleanGetMethodName)
      */
     public function getUseCookies();
+
+    /**
+     * Get save handler name
+     *
+     * @return string
+     */
+    public function getSaveHandlerName();
+
+    /**
+     * Set session.save_handler
+     *
+     * @param string $saveHandler
+     * @return $this
+     */
+    public function setSaveHandler($saveHandler);
 }
diff --git a/lib/internal/Magento/Framework/Session/SaveHandler.php b/lib/internal/Magento/Framework/Session/SaveHandler.php
index 3e7b593dfcf5e73b149898968c7a1281dea08cb1..e4ec505a2cea873f6364e4ecc5472e523127309e 100644
--- a/lib/internal/Magento/Framework/Session/SaveHandler.php
+++ b/lib/internal/Magento/Framework/Session/SaveHandler.php
@@ -5,8 +5,8 @@
  */
 namespace Magento\Framework\Session;
 
-use Magento\Framework\App\DeploymentConfig;
 use Magento\Framework\Exception\SessionException;
+use Magento\Framework\Session\Config\ConfigInterface;
 
 /**
  * Magento session save handler
@@ -24,19 +24,20 @@ class SaveHandler implements SaveHandlerInterface
      * Constructor
      *
      * @param SaveHandlerFactory $saveHandlerFactory
-     * @param DeploymentConfig $deploymentConfig
+     * @param ConfigInterface $config
      * @param string $default
      */
     public function __construct(
         SaveHandlerFactory $saveHandlerFactory,
-        DeploymentConfig $deploymentConfig,
+        ConfigInterface $config,
         $default = self::DEFAULT_HANDLER
     ) {
-        $saveMethod = $deploymentConfig->get(\Magento\Framework\Session\Config::PARAM_SESSION_SAVE_METHOD);
+        $saveMethod = $config->getSaveHandlerName();
         try {
             $connection = $saveHandlerFactory->create($saveMethod);
         } catch (SessionException $e) {
             $connection = $saveHandlerFactory->create($default);
+            $config->setSaveHandler($default);
         }
         $this->saveHandlerAdapter = $connection;
     }
diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php
new file mode 100644
index 0000000000000000000000000000000000000000..56ce5cc075d7f296303768479b2059308b567f6d
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Session\SaveHandler;
+
+use Cm\RedisSession\Handler\ConfigInterface;
+use Cm\RedisSession\Handler\LoggerInterface;
+use Cm\RedisSession\ConnectionFailedException;
+use Cm\RedisSession\ConcurrentConnectionsExceededException;
+use Magento\Framework\Exception\SessionException;
+use Magento\Framework\Phrase;
+use Magento\Framework\Filesystem;
+use Magento\Framework\App\Filesystem\DirectoryList;
+
+class Redis extends \Cm\RedisSession\Handler
+{
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    /**
+     * @param ConfigInterface $config
+     * @param LoggerInterface $logger
+     * @param Filesystem $filesystem
+     * @throws SessionException
+     */
+    public function __construct(ConfigInterface $config, LoggerInterface $logger, Filesystem $filesystem)
+    {
+        $this->filesystem = $filesystem;
+        try {
+            parent::__construct($config, $logger);
+        } catch (ConnectionFailedException $e) {
+            throw new SessionException(new Phrase($e->getMessage()));
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        try {
+            return parent::read($sessionId);
+        } catch (ConcurrentConnectionsExceededException $e) {
+            require $this->filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/503.php');
+        }
+    }
+}
diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..a20878c4026016db3c31f9dc780a980e0b1590ca
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Session\SaveHandler\Redis;
+
+use Magento\Framework\App\DeploymentConfig;
+use Magento\Framework\App\State;
+
+class Config implements \Cm\RedisSession\Handler\ConfigInterface
+{
+    /**
+     * Configuration path for log level
+     */
+    const PARAM_LOG_LEVEL               = 'session/redis/log_level';
+
+    /**
+     * Configuration path for host
+     */
+    const PARAM_HOST                    = 'session/redis/host';
+
+    /**
+     * Configuration path for port
+     */
+    const PARAM_PORT                    = 'session/redis/port';
+
+    /**
+     * Configuration path for database
+     */
+    const PARAM_DATABASE                = 'session/redis/database';
+
+    /**
+     * Configuration path for password
+     */
+    const PARAM_PASSWORD                = 'session/redis/password';
+
+    /**
+     * Configuration path for connection timeout
+     */
+    const PARAM_TIMEOUT                 = 'session/redis/timeout';
+
+    /**
+     * Configuration path for persistent identifier
+     */
+    const PARAM_PERSISTENT_IDENTIFIER   = 'session/redis/param_persistent_identifier';
+
+    /**
+     * Configuration path for compression threshold
+     */
+    const PARAM_COMPRESSION_THRESHOLD   = 'session/redis/param_compression_threshold';
+
+    /**
+     * Configuration path for compression library
+     */
+    const PARAM_COMPRESSION_LIBRARY     = 'session/redis/compression_library';
+
+    /**
+     * Configuration path for maximum number of processes that can wait for a lock on one session
+     */
+    const PARAM_MAX_CONCURRENCY         = 'session/redis/max_concurrency';
+
+    /**
+     * Configuration path for minimum session lifetime
+     */
+    const PARAM_MAX_LIFETIME            = 'session/redis/max_lifetime';
+
+    /**
+     * Configuration path for min
+     */
+    const PARAM_MIN_LIFETIME            = 'session/redis/min_lifetime';
+
+    /**
+     * Configuration path for disabling session locking entirely flag
+     */
+    const PARAM_DISABLE_LOCKING         = 'session/redis/disable_locking';
+
+    /**
+     * Configuration path for lifetime of session for bots on subsequent writes
+     */
+    const PARAM_BOT_LIFETIME            = 'session/redis/bot_lifetime';
+
+    /**
+     * Configuration path for lifetime of session for bots on the first write
+     */
+    const PARAM_BOT_FIRST_LIFETIME      = 'session/redis/bot_first_lifetime';
+
+    /**
+     * Configuration path for lifetime of session for non-bots on the first write
+     */
+    const PARAM_FIRST_LIFETIME          = 'session/redis/first_lifetime';
+
+    /**
+     * Configuration path for number of seconds to wait before trying to break the lock
+     */
+    const PARAM_BREAK_AFTER             = 'session/redis/break_after';
+
+    /**
+     * Deployment config
+     *
+     * @var DeploymentConfig $deploymentConfig
+     */
+    private $deploymentConfig;
+
+    /**
+     * @param DeploymentConfig $deploymentConfig
+     * @param State $appState
+     */
+    public function __construct(DeploymentConfig $deploymentConfig, State $appState)
+    {
+        $this->deploymentConfig = $deploymentConfig;
+        $this->appState = $appState;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLogLevel()
+    {
+        return $this->deploymentConfig->get(self::PARAM_LOG_LEVEL);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getHost()
+    {
+        return $this->deploymentConfig->get(self::PARAM_HOST);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPort()
+    {
+        return $this->deploymentConfig->get(self::PARAM_PORT);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDatabase()
+    {
+        return $this->deploymentConfig->get(self::PARAM_DATABASE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPassword()
+    {
+        return $this->deploymentConfig->get(self::PARAM_PASSWORD);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTimeout()
+    {
+        return $this->deploymentConfig->get(self::PARAM_TIMEOUT);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPersistentIdentifier()
+    {
+        return $this->deploymentConfig->get(self::PARAM_PERSISTENT_IDENTIFIER);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCompressionThreshold()
+    {
+        return $this->deploymentConfig->get(self::PARAM_COMPRESSION_THRESHOLD);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCompressionLibrary()
+    {
+        return $this->deploymentConfig->get(self::PARAM_COMPRESSION_LIBRARY);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMaxConcurrency()
+    {
+        return $this->deploymentConfig->get(self::PARAM_MAX_CONCURRENCY);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMaxLifetime()
+    {
+        return $this->deploymentConfig->get(self::PARAM_MAX_LIFETIME);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMinLifetime()
+    {
+        return $this->deploymentConfig->get(self::PARAM_MIN_LIFETIME);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDisableLocking()
+    {
+        return $this->deploymentConfig->get(self::PARAM_DISABLE_LOCKING);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBotLifetime()
+    {
+        return $this->deploymentConfig->get(self::PARAM_BOT_LIFETIME);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBotFirstLifetime()
+    {
+        return $this->deploymentConfig->get(self::PARAM_BOT_FIRST_LIFETIME);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFirstLifetime()
+    {
+        return $this->deploymentConfig->get(self::PARAM_FIRST_LIFETIME);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBreakAfter()
+    {
+        return $this->deploymentConfig->get(self::PARAM_BREAK_AFTER . '_' . $this->appState->getAreaCode());
+    }
+}
diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Logger.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Logger.php
new file mode 100644
index 0000000000000000000000000000000000000000..8684f475de5f648d3b912d41fe47a3a886456c2f
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Logger.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Session\SaveHandler\Redis;
+
+use Cm\RedisSession\Handler\ConfigInterface;
+use Psr\Log\LoggerInterface;
+use Magento\Framework\App\Request\Http as Request;
+
+class Logger implements \Cm\RedisSession\Handler\LoggerInterface
+{
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var int
+     */
+    private $logLevel;
+
+    /**
+     * @var Request
+     */
+    private $request;
+
+    /**
+     * Logger constructor
+     *
+     * @param ConfigInterface $config
+     * @param LoggerInterface $logger
+     * @param Request $request
+     */
+    public function __construct(ConfigInterface $config, LoggerInterface $logger, Request $request)
+    {
+        $this->logger = $logger;
+        $this->request = $request;
+        $this->logLevel = $config->getLogLevel() ?: self::ALERT;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setLogLevel($level)
+    {
+        $this->logLevel = $level;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function log($message, $level)
+    {
+        $message .= ' ' . $this->request->getRequestUri();
+        if ($this->logLevel >= $level) {
+            switch ($level) {
+                case self::EMERGENCY:
+                    $this->logger->emergency($message);
+                    break;
+                case self::ALERT:
+                    $this->logger->alert($message);
+                    break;
+                case self::CRITICAL:
+                    $this->logger->critical($message);
+                    break;
+                case self::ERROR:
+                    $this->logger->error($message);
+                    break;
+                case self::WARNING:
+                    $this->logger->warning($message);
+                    break;
+                case self::NOTICE:
+                    $this->logger->notice($message);
+                    break;
+                case self::INFO:
+                    $this->logger->info($message);
+                    break;
+                default:
+                    $this->logger->debug($message);
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function logException(\Exception $e)
+    {
+        $this->logger->critical($e->getMessage());
+    }
+}
diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
index 6c43bf72c0d010fe2eee5d71622e33ee1044b6d6..3c50426ce22347baf077bcf3f50739b5fa73bb6d 100644
--- a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
+++ b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php
@@ -148,8 +148,12 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
     public function testSaveHandlerIsMutable()
     {
         $this->getModel($this->validatorMock);
-        $this->config->setSaveHandler('user');
+        $this->config->setSaveHandler('redis');
         $this->assertEquals('user', $this->config->getSaveHandler());
+        $this->assertEquals('redis', $this->config->getSaveHandlerName());
+        $this->config->setSaveHandler('files');
+        $this->assertEquals('files', $this->config->getSaveHandler());
+        $this->assertEquals('files', $this->config->getSaveHandlerName());
     }
 
     public function testCookieLifetimeIsMutable()
diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..30b8787753c0e2bca4397d1dd4170e62a0692b75
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php
@@ -0,0 +1,214 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Session\Test\Unit\SaveHandler\Redis;
+
+use Magento\Framework\Session\SaveHandler\Redis\Config;
+
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\App\DeploymentConfig
+     */
+    protected $deploymentConfig;
+
+    /**
+     * @var \Magento\Framework\App\State
+     */
+    protected $appState;
+
+    /**
+     * @var \Magento\Framework\Session\SaveHandler\Redis\Config
+     */
+    protected $config;
+
+    public function setUp()
+    {
+        $this->deploymentConfig = $this->getMock('Magento\Framework\App\DeploymentConfig', [], [], '', false);
+        $this->appState = $this->getMock('Magento\Framework\App\State', [], [], '', false);
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->config = $objectManager->getObject(
+            'Magento\Framework\Session\SaveHandler\Redis\Config',
+            [
+                'deploymentConfig' => $this->deploymentConfig,
+                'appState' => $this->appState
+            ]
+        );
+    }
+
+    public function testGetLogLevel()
+    {
+        $expected = 2;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_LOG_LEVEL)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getLogLevel(), $expected);
+    }
+
+    public function testGetHost()
+    {
+        $expected = '127.0.0.1';
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_HOST)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getHost(), $expected);
+    }
+
+    public function testGetPort()
+    {
+        $expected = 1234;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_PORT)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getPort(), $expected);
+    }
+
+    public function testGetDatabase()
+    {
+        $expected = 2;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_DATABASE)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getDatabase(), $expected);
+    }
+
+    public function testGetPassword()
+    {
+        $expected = 'password';
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_PASSWORD)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getPassword(), $expected);
+    }
+
+    public function testGetTimeout()
+    {
+        $expected = 10;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_TIMEOUT)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getTimeout(), $expected);
+    }
+
+    public function testGetPersistentIdentifier()
+    {
+        $expected = 'sess01';
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_PERSISTENT_IDENTIFIER)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getPersistentIdentifier(), $expected);
+    }
+
+    public function testGetCompressionThreshold()
+    {
+        $expected = 2;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_COMPRESSION_THRESHOLD)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getCompressionThreshold(), $expected);
+    }
+
+    public function testGetCompressionLibrary()
+    {
+        $expected = 'gzip';
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_COMPRESSION_LIBRARY)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getCompressionLibrary(), $expected);
+    }
+
+    public function testGetMaxConcurrency()
+    {
+        $expected = 6;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_MAX_CONCURRENCY)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getMaxConcurrency(), $expected);
+    }
+
+    public function testGetMaxLifetime()
+    {
+        $expected = 30;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_MAX_LIFETIME)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getMaxLifetime(), $expected);
+    }
+
+    public function testGetMinLifetime()
+    {
+        $expected = 30;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_MIN_LIFETIME)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getMinLifetime(), $expected);
+    }
+
+    public function testGetDisableLocking()
+    {
+        $expected = false;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_DISABLE_LOCKING)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getDisableLocking(), $expected);
+    }
+
+    public function testGetBotLifetime()
+    {
+        $expected = 30;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_BOT_LIFETIME)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getBotLifetime(), $expected);
+    }
+
+    public function testGetBotFirstLifetime()
+    {
+        $expected = 30;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_BOT_FIRST_LIFETIME)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getBotFirstLifetime(), $expected);
+    }
+
+    public function testGetFirstLifetime()
+    {
+        $expected = 30;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_FIRST_LIFETIME)
+            ->willReturn($expected);
+        $this->assertEquals($this->config->getFirstLifetime(), $expected);
+    }
+
+    public function testBreakAfter()
+    {
+        $areaCode = 'frontend';
+        $breakAfter = 5;
+        $this->deploymentConfig->expects($this->once())
+            ->method('get')
+            ->with(Config::PARAM_BREAK_AFTER . '_' . $areaCode)
+            ->willReturn($breakAfter);
+        $this->appState->expects($this->once())
+            ->method('getAreaCode')
+            ->willReturn($areaCode);
+        $this->assertEquals($this->config->getBreakAfter(), $breakAfter);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d48bea62b542903620debc3c4f4a9babbd884f14
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Session\Test\Unit\SaveHandler\Redis;
+
+use Cm\RedisSession\Handler\LoggerInterface;
+use Magento\Framework\Session\SaveHandler\Redis\Logger;
+
+class LoggerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Cm\RedisSession\Handler\ConfigInterface
+     */
+    protected $config;
+
+    /**
+     * @var \Psr\Log\LoggerInterface
+     */
+    protected $psrLogger;
+
+    /**
+     * @var \Magento\Framework\Session\SaveHandler\Redis\Logger
+     */
+    protected $logger;
+
+    /**
+     * @var \Magento\Framework\App\Request\Http
+     */
+    protected $request;
+
+    /**
+     * @var string
+     */
+    protected $requestUri = 'customer/account/login';
+
+    public function setUp()
+    {
+        $this->config = $this->getMock('Cm\RedisSession\Handler\ConfigInterface', [], [], '', false);
+        $this->config->expects($this->once())
+            ->method('getLogLevel')
+            ->willReturn(LoggerInterface::DEBUG);
+        $this->psrLogger = $this->getMock('Psr\Log\LoggerInterface', [], [], '', false);
+        $this->request = $this->getMock('Magento\Framework\App\Request\Http', [], [], '', false);
+        //$this->logger = new Logger($this->config, $this->psrLogger, $this->request);
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->logger = $objectManager->getObject(
+            'Magento\Framework\Session\SaveHandler\Redis\Logger',
+            [
+                'config' => $this->config,
+                'logger' => $this->psrLogger,
+                'request' => $this->request
+            ]
+        );
+    }
+
+    /**
+     * @dataProvider logDataProvider
+     */
+    public function testLog($logLevel, $method)
+    {
+        $message = 'Error message';
+        $this->request->expects($this->once())
+            ->method('getRequestUri')
+            ->willReturn($this->requestUri);
+        $this->psrLogger->expects($this->once())
+            ->method($method)
+            ->with($message . ' ' . $this->requestUri);
+        $this->logger->log($message, $logLevel);
+    }
+
+    public function logDataProvider()
+    {
+        return [
+            [LoggerInterface::EMERGENCY, 'emergency'],
+            [LoggerInterface::ALERT, 'alert'],
+            [LoggerInterface::CRITICAL, 'critical'],
+            [LoggerInterface::ERROR, 'error'],
+            [LoggerInterface::WARNING, 'warning'],
+            [LoggerInterface::NOTICE, 'notice'],
+            [LoggerInterface::INFO, 'info'],
+            [LoggerInterface::DEBUG, 'debug'],
+        ];
+    }
+
+    public function testLogException()
+    {
+        $exception = new \Exception('Error message');
+        $this->psrLogger->expects($this->once())
+            ->method('critical')
+            ->with($exception->getMessage());
+        $this->logger->logException($exception);
+    }
+}
diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/SelectRendererTrait.php b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/SelectRendererTrait.php
index 8089f288c3c7211da352a1b5d1450f539c922265..24d160d07d3dbdc8c22f7c0e7c8f0053e1be2f12 100644
--- a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/SelectRendererTrait.php
+++ b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/SelectRendererTrait.php
@@ -26,6 +26,7 @@ trait SelectRendererTrait
                             'Magento\Framework\DB\Select\DistinctRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'distinct',
                     ],
                     'columns' => [
                         'renderer' => $objectManager->getObject(
@@ -35,12 +36,14 @@ trait SelectRendererTrait
                             ]
                         ),
                         'sort' => 11,
+                        'part' => 'columns',
                     ],
                     'union' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\UnionRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'union',
                     ],
                     'from' => [
                         'renderer' => $objectManager->getObject(
@@ -50,36 +53,42 @@ trait SelectRendererTrait
                             ]
                         ),
                         'sort' => 11,
+                        'part' => 'from',
                     ],
                     'where' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\WhereRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'where',
                     ],
                     'group' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\GroupRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'group',
                     ],
                     'having' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\HavingRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'having',
                     ],
                     'order' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\OrderRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'order',
                     ],
                     'limit' => [
                         'renderer' => $objectManager->getObject(
                             'Magento\Framework\DB\Select\LimitRenderer'
                         ),
                         'sort' => 11,
+                        'part' => 'limitcount',
                     ],
                 ],
             ]
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
similarity index 99%
rename from setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php
rename to setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
index 27454c09f6177b71a63d6c01ca92703eb16f10b5..f3f55090dee5eeec5f829d16613c36d2d8749aff 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
@@ -4,7 +4,7 @@
  * See COPYING.txt for license details.
  */
 
-namespace Magento\Setup\Test\Unit\Module;
+namespace Magento\Setup\Test\Unit\Model;
 
 use Magento\Setup\Model\ConfigGenerator;
 use Magento\Setup\Model\ConfigOptionsList;
@@ -149,7 +149,7 @@ class ConfigOptionsListTest extends \PHPUnit_Framework_TestCase
         $this->dbValidator->expects($this->once())->method('checkDatabaseTablePrefix')->willReturn($configDataMock);
         $this->dbValidator->expects($this->once())->method('checkDatabaseConnection')->willReturn($configDataMock);
     }
-    
+
     /**
      * @param string $hosts
      * @param bool $expectedError