diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index 54208dcdba534139c96e8fd975e0757f9ecb11e1..ceb5580307c22e07486effd290c61e88f768c84a 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -1416,7 +1416,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
     }
 
     /**
-     * Get existing images for current bucnh
+     * Get existing images for current bunch
      *
      * @param array $bunch
      * @return array
@@ -1436,7 +1436,21 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
         )->joinInner(
             ['mgvte' => $this->mediaGalleryEntityToValueTableName],
             '(mg.value_id = mgvte.value_id)',
-            [$this->getProductEntityLinkField() => 'mgvte.' . $this->getProductEntityLinkField()]
+            [
+                $this->getProductEntityLinkField() => 'mgvte.' . $this->getProductEntityLinkField(),
+                'value_id' => 'mgvte.value_id'
+            ]
+        )->joinLeft(
+            ['mgv' => $this->mediaGalleryValueTableName],
+            sprintf(
+                '(mg.value_id = mgv.value_id AND mgv.%s = mgvte.%s AND mgv.store_id = %d)',
+                $this->getProductEntityLinkField(),
+                $this->getProductEntityLinkField(),
+                \Magento\Store\Model\Store::DEFAULT_STORE_ID
+            ),
+            [
+                'label' => 'mgv.label'
+            ]
         )->joinInner(
             ['pe' => $this->productEntityTableName],
             "(mgvte.{$this->getProductEntityLinkField()} = pe.{$this->getProductEntityLinkField()})",
@@ -1447,7 +1461,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
         );
 
         foreach ($this->_connection->fetchAll($select) as $image) {
-            $result[$image['sku']][$image['value']] = true;
+            $result[$image['sku']][$image['value']] = $image;
         }
 
         return $result;
@@ -1462,22 +1476,21 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
         $images = [];
         $labels = [];
         foreach ($this->_imagesArrayKeys as $column) {
-            $images[$column] = [];
-            $labels[$column] = [];
             if (!empty($rowData[$column])) {
                 $images[$column] = array_unique(
-                    explode($this->getMultipleValueSeparator(), $rowData[$column])
+                    array_map(
+                        'trim',
+                        explode($this->getMultipleValueSeparator(), $rowData[$column])
+                    )
                 );
-            }
 
-            if (!empty($rowData[$column . '_label'])) {
-                $labels[$column] = explode($this->getMultipleValueSeparator(), $rowData[$column . '_label']);
-            }
+                if (!empty($rowData[$column . '_label'])) {
+                    $labels[$column] = $this->parseMultipleValues($rowData[$column . '_label']);
 
-            if (count($labels[$column]) > count($images[$column])) {
-                $labels[$column] = array_slice($labels[$column], 0, count($images[$column]));
-            } elseif (count($labels[$column]) < count($images[$column])) {
-                $labels[$column] = array_pad($labels[$column], count($images[$column]), '');
+                    if (count($labels[$column]) > count($images[$column])) {
+                        $labels[$column] = array_slice($labels[$column], 0, count($images[$column]));
+                    }
+                }
             }
         }
 
@@ -1507,6 +1520,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
             $this->categoriesCache = [];
             $tierPrices = [];
             $mediaGallery = [];
+            $labelsForUpdate = [];
             $uploadedImages = [];
             $previousType = null;
             $prevAttributeSet = null;
@@ -1616,7 +1630,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
                 foreach ($rowImages as $column => $columnImages) {
                     foreach ($columnImages as $position => $columnImage) {
                         if (!isset($uploadedImages[$columnImage])) {
-                            $uploadedFile = $this->uploadMediaFiles(trim($columnImage), true);
+                            $uploadedFile = $this->uploadMediaFiles($columnImage, true);
                             if ($uploadedFile) {
                                 $uploadedImages[$columnImage] = $uploadedFile;
                             } else {
@@ -1636,20 +1650,28 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
                             $rowData[$column] = $uploadedFile;
                         }
 
-                        $imageNotAssigned = !isset($existingImages[$rowSku][$uploadedFile]);
-
-                        if ($uploadedFile && $imageNotAssigned) {
-                            if ($column == self::COL_MEDIA_IMAGE) {
-                                $rowData[$column][] = $uploadedFile;
+                        if ($uploadedFile && !isset($mediaGallery[$rowSku][$uploadedFile])) {
+                            if (isset($existingImages[$rowSku][$uploadedFile])) {
+                                if (isset($rowLabels[$column][$position])
+                                    && $rowLabels[$column][$position] != $existingImages[$rowSku][$uploadedFile]['label']
+                                ) {
+                                    $labelsForUpdate[] = [
+                                        'label' => $rowLabels[$column][$position],
+                                        'imageData' => $existingImages[$rowSku][$uploadedFile]
+                                    ];
+                                }
+                            } else {
+                                if ($column == self::COL_MEDIA_IMAGE) {
+                                    $rowData[$column][] = $uploadedFile;
+                                }
+                                $mediaGallery[$rowSku][$uploadedFile] = [
+                                    'attribute_id' => $this->getMediaGalleryAttributeId(),
+                                    'label' => isset($rowLabels[$column][$position]) ? $rowLabels[$column][$position] : '',
+                                    'position' => $position + 1,
+                                    'disabled' => isset($disabledImages[$columnImage]) ? '1' : '0',
+                                    'value' => $uploadedFile,
+                                ];
                             }
-                            $mediaGallery[$rowSku][] = [
-                                'attribute_id' => $this->getMediaGalleryAttributeId(),
-                                'label' => isset($rowLabels[$column][$position]) ? $rowLabels[$column][$position] : '',
-                                'position' => $position + 1,
-                                'disabled' => isset($disabledImages[$columnImage]) ? '1' : '0',
-                                'value' => $uploadedFile,
-                            ];
-                            $existingImages[$rowSku][$uploadedFile] = true;
                         }
                     }
                 }
@@ -1767,6 +1789,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
                 $mediaGallery
             )->_saveProductAttributes(
                 $attributes
+            )->updateMediaGalleryLabels(
+                $labelsForUpdate
             );
 
             $this->_eventManager->dispatch(
@@ -2535,12 +2559,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
      * Parse values of multiselect attributes depends on "Fields Enclosure" parameter
      *
      * @param string $values
+     * @param string $delimiter
      * @return array
      */
-    public function parseMultiselectValues($values)
+    public function parseMultiselectValues($values, $delimiter = self::PSEUDO_MULTI_LINE_SEPARATOR)
     {
         if (empty($this->_parameters[Import::FIELDS_ENCLOSURE])) {
-            return explode(self::PSEUDO_MULTI_LINE_SEPARATOR, $values);
+            return explode($delimiter, $values);
         }
         if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) {
             return $values = array_map(function ($value) {
@@ -2752,4 +2777,64 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
         }
         return $this->productEntityIdentifierField;
     }
+
+    /**
+     * Update media gallery labels
+     *
+     * @param array $labels
+     * @return void
+     */
+    private function updateMediaGalleryLabels(array $labels)
+    {
+        if (empty($labels)) {
+            return;
+        }
+
+        $insertData = [];
+        foreach ($labels as $label) {
+            $imageData = $label['imageData'];
+
+            if ($imageData['label'] === null) {
+                $insertData[] = [
+                    'label' => $label['label'],
+                    $this->getProductEntityLinkField() => $imageData[$this->getProductEntityLinkField()],
+                    'value_id' => $imageData['value_id'],
+                    'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID
+                ];
+            } else {
+                $this->_connection->update(
+                    $this->mediaGalleryValueTableName,
+                    [
+                        'label' => $label['label']
+                    ],
+                    [
+                        $this->getProductEntityLinkField() . ' = ?' => $imageData[$this->getProductEntityLinkField()],
+                        'value_id = ?' => $imageData['value_id'],
+                        'store_id = ?' => \Magento\Store\Model\Store::DEFAULT_STORE_ID
+                    ]
+                );
+            }
+        }
+
+        if (!empty($insertData)) {
+            $this->_connection->insertMultiple(
+                $this->mediaGalleryValueTableName,
+                $insertData
+            );
+        }
+    }
+
+    /**
+     * Parse values from multiple attributes fields
+     *
+     * @param string $labelRow
+     * @return array
+     */
+    private function parseMultipleValues($labelRow)
+    {
+        return $this->parseMultiselectValues(
+            $labelRow,
+            $this->getMultipleValueSeparator()
+        );
+    }
 }
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php
index 0b728ee5038873ddbb7a56eab967fcdf53c6c419..3067aa3c2b2eb29eb032201b0c6d062ab54baa08 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php
@@ -5,7 +5,6 @@
  */
 namespace Magento\CatalogImportExport\Model\Import\Product\Validator;
 
-use Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractImportValidator;
 use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface;
 
 class Media extends AbstractImportValidator implements RowValidatorInterface
@@ -16,19 +15,15 @@ class Media extends AbstractImportValidator implements RowValidatorInterface
 
     const ADDITIONAL_IMAGES = 'additional_images';
 
+    /**
+     * @deprecated
+     * @see \Magento\CatalogImportExport\Model\Import\Product::getMultipleValueSeparator()
+     */
     const ADDITIONAL_IMAGES_DELIMITER = ',';
 
     /** @var array */
     protected $mediaAttributes = ['image', 'small_image', 'thumbnail'];
 
-    /**
-     * {@inheritdoc}
-     */
-    public function init($context)
-    {
-        return parent::init($context);
-    }
-
     /**
      * @param string $string
      * @return bool
@@ -83,7 +78,7 @@ class Media extends AbstractImportValidator implements RowValidatorInterface
             }
         }
         if (isset($value[self::ADDITIONAL_IMAGES]) && strlen($value[self::ADDITIONAL_IMAGES])) {
-            foreach (explode(self::ADDITIONAL_IMAGES_DELIMITER, $value[self::ADDITIONAL_IMAGES]) as $image) {
+            foreach (explode($this->context->getMultipleValueSeparator(), $value[self::ADDITIONAL_IMAGES]) as $image) {
                 if (!$this->checkPath($image) && !$this->checkValidUrl($image)) {
                     $this->_addMessages(
                         [
diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/MediaTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/MediaTest.php
index a4a937f25cf81334e19ef1aa079c1d274f79bacd..df7b33c72995b1a7a78faa6f0d5d74daf7ce8a5e 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/MediaTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/MediaTest.php
@@ -6,11 +6,14 @@
 
 namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product\Validator;
 
+use Magento\CatalogImportExport\Model\Import\Product;
+use Magento\CatalogImportExport\Model\Import\Product\Validator\Media;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\ImportExport\Model\Import;
 
 class MediaTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var \Magento\CatalogImportExport\Model\Import\Product\Validator\Media */
+    /** @var Media */
     protected $media;
 
     /** @var ObjectManagerHelper */
@@ -21,7 +24,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase
         
         $this->objectManagerHelper = new ObjectManagerHelper($this);
         $this->media = $this->objectManagerHelper->getObject(
-            \Magento\CatalogImportExport\Model\Import\Product\Validator\Media::class,
+            Media::class,
             [
                 
             ]
@@ -41,6 +44,18 @@ class MediaTest extends \PHPUnit_Framework_TestCase
      */
     public function testIsValid($data, $expected)
     {
+        $contextMock = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $contextMock->expects($this->any())
+            ->method('getMultipleValueSeparator')
+            ->willReturn(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR);
+        $contextMock->expects($this->any())
+            ->method('retrieveMessageTemplate')
+            ->with(Media::ERROR_INVALID_MEDIA_URL_OR_PATH)
+            ->willReturn('%s');
+        $this->media->init($contextMock);
+
         $result = $this->media->isValid($data);
         $this->assertEquals($expected['result'], $result);
         $messages = $this->media->getMessages();
@@ -50,7 +65,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase
     public function testIsValidClearMessagesCall()
     {
         $media = $this->getMock(
-            \Magento\CatalogImportExport\Model\Import\Product\Validator\Media::class,
+            Media::class,
             ['_clearMessages'],
             [],
             '',
@@ -78,6 +93,14 @@ class MediaTest extends \PHPUnit_Framework_TestCase
             'invalid' => [
                 ['_media_image' => 1],
                 ['result' => true,'messages' => []],
+            ],
+            'additional_images' => [
+                ['additional_images' => 'image1.png,image2.jpg'],
+                ['result' => true, 'messages' => []]
+            ],
+            'additional_images_fail' => [
+                ['additional_images' => 'image1.png|image2.jpg|image3.gif'],
+                ['result' => false, 'messages' => [0 => 'additional_images']]
             ]
         ];
     }
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 bf9c694d949d44709be47b73f08dd4eeb2c50c18..cd1fedf82fe85a4b0ea0b55ff056c38cd5ee8e4c 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
@@ -1280,6 +1280,43 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
         $importProduct->validateRow($rowData, $rowNum);
     }
 
+    /**
+     * @dataProvider getImagesFromRowDataProvider
+     */
+    public function testGetImagesFromRow($rowData, $expectedResult)
+    {
+        $this->assertEquals(
+            $this->importProduct->getImagesFromRow($rowData),
+            $expectedResult
+        );
+    }
+
+    public function getImagesFromRowDataProvider()
+    {
+        return [
+            [
+                [],
+                [[], []]
+            ],
+            [
+                [
+                    'image' => 'image3.jpg',
+                    '_media_image' => 'image1.jpg,image2.png',
+                    '_media_image_label' => 'label1,label2'
+                ],
+                [
+                    [
+                        'image' => ['image3.jpg'],
+                        '_media_image' => ['image1.jpg', 'image2.png']
+                    ],
+                    [
+                        '_media_image' => ['label1', 'label2']
+                    ],
+                ]
+            ]
+        ];
+    }
+
     public function validateRowValidateNewProductTypeAddRowErrorCallDataProvider()
     {
         return [
diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
index 21b085c4c419f0d03bcb72389627ec665582f298..cb30f7abc71792b72d0b78e24b4ba89e25303d67 100644
--- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
+++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
@@ -42,13 +42,18 @@ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface
     /**
      * @param \Magento\Framework\Event\Observer $observer
      * @return void
+     * @throws \Magento\Framework\Exception\LocalizedException
      */
     public function execute(\Magento\Framework\Event\Observer $observer)
     {
         /** @var Category $category */
         $category = $observer->getEvent()->getCategory();
         if ($category->getUrlKey() !== false) {
-            $category->setUrlKey($this->categoryUrlPathGenerator->getUrlKey($category))
+            $resultUrlKey = $this->categoryUrlPathGenerator->getUrlKey($category);
+            if (empty($resultUrlKey)) {
+                throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key'));
+            }
+            $category->setUrlKey($resultUrlKey)
                 ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
             if (!$category->isObjectNew()) {
                 $category->getResource()->saveAttribute($category, 'url_path');
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
index f2d10d59e02c499fca965ce2a38a03804adc3654..45a74260e927001bcf39148dc2f5d9a9574d0189 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
@@ -30,18 +30,38 @@ class Save extends \Magento\Backend\App\Action
      */
     protected $dataPersistor;
 
+    /**
+     * @var \Magento\Cms\Model\PageFactory
+     */
+    private $pageFactory;
+
+    /**
+     * @var \Magento\Cms\Api\PageRepositoryInterface
+     */
+    private $pageRepository;
+
     /**
      * @param Action\Context $context
      * @param PostDataProcessor $dataProcessor
      * @param DataPersistorInterface $dataPersistor
+     * @param \Magento\Cms\Model\PageFactory $pageFactory
+     * @param \Magento\Cms\Api\PageRepositoryInterface $pageRepository
+     *
      */
     public function __construct(
         Action\Context $context,
         PostDataProcessor $dataProcessor,
-        DataPersistorInterface $dataPersistor
+        DataPersistorInterface $dataPersistor,
+        \Magento\Cms\Model\PageFactory $pageFactory = null,
+        \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null
     ) {
         $this->dataProcessor = $dataProcessor;
         $this->dataPersistor = $dataPersistor;
+        $this->pageFactory = $pageFactory
+            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class);
+        $this->pageRepository = $pageRepository
+            ?: \Magento\Framework\App\ObjectManager::getInstance()
+                ->get(\Magento\Cms\Api\PageRepositoryInterface::class);
         parent::__construct($context);
     }
 
@@ -66,7 +86,7 @@ class Save extends \Magento\Backend\App\Action
             }
 
             /** @var \Magento\Cms\Model\Page $model */
-            $model = $this->_objectManager->create(\Magento\Cms\Model\Page::class);
+            $model = $this->pageFactory->create();
 
             $id = $this->getRequest()->getParam('page_id');
             if ($id) {
@@ -85,7 +105,7 @@ class Save extends \Magento\Backend\App\Action
             }
 
             try {
-                $model->save();
+                $this->pageRepository->save($model);
                 $this->messageManager->addSuccess(__('You saved the page.'));
                 $this->dataPersistor->clear('cms_page');
                 if ($this->getRequest()->getParam('back')) {
diff --git a/app/code/Magento/Cms/Setup/UpgradeData.php b/app/code/Magento/Cms/Setup/UpgradeData.php
index 41a28989439af2e9ae14fe270b603e7e8c254abb..6d22f782b25b130b0cca1174e5386e7fb92cd5a7 100644
--- a/app/code/Magento/Cms/Setup/UpgradeData.php
+++ b/app/code/Magento/Cms/Setup/UpgradeData.php
@@ -13,6 +13,9 @@ use Magento\Framework\Setup\UpgradeDataInterface;
 
 class UpgradeData implements UpgradeDataInterface
 {
+    /**
+     * @deprecated
+     */
     const PRIVACY_COOKIE_PAGE_ID = 4;
 
     /**
@@ -234,7 +237,10 @@ class UpgradeData implements UpgradeDataInterface
     </table>
 </div>
 EOD;
-            $privacyAndCookiePolicyPage = $this->createPage()->load(self::PRIVACY_COOKIE_PAGE_ID);
+            $privacyAndCookiePolicyPage = $this->createPage()->load(
+                'privacy-policy-cookie-restriction-mode',
+                'identifier'
+            );
             $privacyAndCookiePolicyPageId = $privacyAndCookiePolicyPage->getId();
             if ($privacyAndCookiePolicyPageId) {
                 $privacyAndCookiePolicyPage->setContent($newPageContent);
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
index 7495a2ad1bad924c241e9fae564785574f13a3a1..12057d2681c20cf8cbc5822d6180b9df96b095ee 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
@@ -13,73 +13,61 @@ class SaveTest extends \PHPUnit_Framework_TestCase
     /**
      * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $requestMock;
+    private $requestMock;
 
     /**
      * @var \Magento\Cms\Controller\Adminhtml\Page\PostDataProcessor|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $dataProcessorMock;
+    private $dataProcessorMock;
 
     /**
      * @var \Magento\Framework\App\Request\DataPersistorInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $dataPersistorMock;
+    private $dataPersistorMock;
 
     /**
      * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $resultRedirectFactory;
+    private $resultRedirectFactory;
 
     /**
      * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $resultRedirect;
+    private $resultRedirect;
 
     /**
-     * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $contextMock;
-
-    /**
-     * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $objectManagerMock;
-
-    /**
-     * @var \Magento\Cms\Model\Page|\PHPUnit_Framework_MockObject_MockObject $pageMock
+     * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $pageMock;
+    private $messageManagerMock;
 
     /**
-     * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $messageManagerMock;
+    private $eventManagerMock;
 
     /**
-     * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Cms\Model\PageFactory|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $eventManagerMock;
+    private $pageFactory;
 
     /**
-     * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+     * @var \Magento\Cms\Api\PageRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $objectManager;
+    private $pageRepository;
 
     /**
      * @var \Magento\Cms\Controller\Adminhtml\Page\Save
      */
-    protected $saveController;
+    private $saveController;
 
     /**
      * @var int
      */
-    protected $pageId = 1;
+    private $pageId = 1;
 
     protected function setUp()
     {
-        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-
-        $this->contextMock = $this->getMock(\Magento\Backend\App\Action\Context::class, [], [], '', false);
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 
         $this->resultRedirectFactory = $this->getMockBuilder(\Magento\Backend\Model\View\Result\RedirectFactory::class)
             ->disableOriginalConstructor()
@@ -91,69 +79,37 @@ class SaveTest extends \PHPUnit_Framework_TestCase
         $this->resultRedirectFactory->expects($this->atLeastOnce())
             ->method('create')
             ->willReturn($this->resultRedirect);
-
-        $this->dataProcessorMock = $this->getMock(
-            \Magento\Cms\Controller\Adminhtml\Page\PostDataProcessor::class,
-            ['filter'],
-            [],
-            '',
-            false
-        );
-
+        $this->dataProcessorMock = $this->getMockBuilder(
+            \Magento\Cms\Controller\Adminhtml\Page\PostDataProcessor::class
+        )->setMethods(['filter'])->disableOriginalConstructor()->getMock();
         $this->dataPersistorMock = $this->getMockBuilder(\Magento\Framework\App\Request\DataPersistorInterface::class)
             ->getMock();
-
-        $this->requestMock = $this->getMockForAbstractClass(
-            \Magento\Framework\App\RequestInterface::class,
-            [],
-            '',
-            false,
-            true,
-            true,
-            ['getParam', 'getPostValue']
-        );
-
-        $this->pageMock = $this->getMockBuilder(
-            \Magento\Cms\Model\Page::class
-        )->disableOriginalConstructor()->getMock();
-
-        $this->messageManagerMock = $this->getMock(
-            \Magento\Framework\Message\ManagerInterface::class,
-            [],
-            [],
-            '',
-            false
-        );
-
-        $this->eventManagerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Event\ManagerInterface::class,
-            [],
-            '',
-            false,
-            true,
-            true,
-            ['dispatch']
-        );
-
-        $this->objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManager\ObjectManager::class)
+        $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)
+            ->setMethods(['getParam', 'getPostValue'])
+            ->getMockForAbstractClass();
+        $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class)
+            ->getMockForAbstractClass();
+        $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class)
+            ->setMethods(['dispatch'])
+            ->getMockForAbstractClass();
+        $this->pageFactory = $this->getMockBuilder(\Magento\Cms\Model\PageFactory::class)
             ->disableOriginalConstructor()
-            ->setMethods(['get', 'create'])
+            ->setMethods(['create'])
             ->getMock();
-
-        $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock);
-        $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($this->objectManagerMock);
-        $this->contextMock->expects($this->any())->method('getMessageManager')->willReturn($this->messageManagerMock);
-        $this->contextMock->expects($this->any())->method('getEventManager')->willReturn($this->eventManagerMock);
-        $this->contextMock->expects($this->any())
-            ->method('getResultRedirectFactory')
-            ->willReturn($this->resultRedirectFactory);
-
-        $this->saveController = $this->objectManager->getObject(
+        $this->pageRepository = $this->getMockBuilder(\Magento\Cms\Api\PageRepositoryInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $this->saveController = $objectManager->getObject(
             \Magento\Cms\Controller\Adminhtml\Page\Save::class,
             [
-                'context' => $this->contextMock,
+                'request' => $this->requestMock,
+                'messageManager' => $this->messageManagerMock,
+                'eventManager' => $this->eventManagerMock,
+                'resultRedirectFactory' => $this->resultRedirectFactory,
                 'dataProcessor' => $this->dataProcessorMock,
                 'dataPersistor' => $this->dataPersistorMock,
+                'pageFactory' => $this->pageFactory,
+                'pageRepository' => $this->pageRepository
             ]
         );
     }
@@ -190,20 +146,21 @@ class SaveTest extends \PHPUnit_Framework_TestCase
                     ['back', null, false],
                 ]
             );
-
-        $this->objectManagerMock->expects($this->atLeastOnce())
+        $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->pageFactory->expects($this->atLeastOnce())
             ->method('create')
-            ->with($this->equalTo(\Magento\Cms\Model\Page::class))
-            ->willReturn($this->pageMock);
+            ->willReturn($page);
 
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('load')
             ->willReturnSelf();
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('getId')
             ->willReturn(true);
-        $this->pageMock->expects($this->once())->method('setData');
-        $this->pageMock->expects($this->once())->method('save');
+        $page->expects($this->once())->method('setData');
+        $this->pageRepository->expects($this->once())->method('save')->with($page);
 
         $this->dataPersistorMock->expects($this->any())
             ->method('clear')
@@ -240,20 +197,21 @@ class SaveTest extends \PHPUnit_Framework_TestCase
         $this->dataProcessorMock->expects($this->any())
             ->method('filter')
             ->willReturnArgument(0);
-
-        $this->objectManagerMock->expects($this->atLeastOnce())
+        $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->pageFactory->expects($this->atLeastOnce())
             ->method('create')
-            ->with($this->equalTo(\Magento\Cms\Model\Page::class))
-            ->willReturn($this->pageMock);
+            ->willReturn($page);
 
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('load')
             ->willReturnSelf();
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('getId')
             ->willReturn(true);
-        $this->pageMock->expects($this->once())->method('setData');
-        $this->pageMock->expects($this->once())->method('save');
+        $page->expects($this->once())->method('setData');
+        $this->pageRepository->expects($this->once())->method('save')->with($page);
 
         $this->messageManagerMock->expects($this->once())
             ->method('addSuccess')
@@ -286,20 +244,22 @@ class SaveTest extends \PHPUnit_Framework_TestCase
         $this->dataProcessorMock->expects($this->any())
             ->method('filter')
             ->willReturnArgument(0);
-
-        $this->objectManagerMock->expects($this->atLeastOnce())
+        $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->pageFactory->expects($this->atLeastOnce())
             ->method('create')
-            ->with($this->equalTo(\Magento\Cms\Model\Page::class))
-            ->willReturn($this->pageMock);
+            ->willReturn($page);
 
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('load')
             ->willReturnSelf();
-        $this->pageMock->expects($this->any())
+        $page->expects($this->any())
             ->method('getId')
             ->willReturn(true);
-        $this->pageMock->expects($this->once())->method('setData');
-        $this->pageMock->expects($this->once())->method('save')->willThrowException(new \Exception('Error message.'));
+        $page->expects($this->once())->method('setData');
+        $this->pageRepository->expects($this->once())->method('save')->with($page)
+            ->willThrowException(new \Exception('Error message.'));
 
         $this->messageManagerMock->expects($this->never())
             ->method('addSuccess');
diff --git a/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlRewriteGeneratorTest.php b/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlRewriteGeneratorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..96a964c3d2d33aa7b0e49baec1db61666718a6a9
--- /dev/null
+++ b/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlRewriteGeneratorTest.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CmsUrlRewrite\Test\Unit\Model;
+
+
+class CmsPageUrlRewriteGeneratorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeManager;
+
+    /**
+     * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlRewriteFactory;
+
+    /**
+     * @var \Magento\CmsUrlRewrite\Model\CmsPageUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlPathGenerator;
+
+    /**
+     * @var \Magento\CmsUrlRewrite\Model\CmsPageUrlRewriteGenerator
+     */
+    private $urlRewriteGenerator;
+
+    /**
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)
+            ->getMockForAbstractClass();
+        $this->urlRewriteFactory = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory::class)
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->urlPathGenerator = $this->getMockBuilder(\Magento\CmsUrlRewrite\Model\CmsPageUrlPathGenerator::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->urlRewriteGenerator = $this->objectManager->getObject(
+            \Magento\CmsUrlRewrite\Model\CmsPageUrlRewriteGenerator::class,
+            [
+                'storeManager' => $this->storeManager,
+                'urlRewriteFactory' => $this->urlRewriteFactory,
+                'cmsPageUrlPathGenerator' => $this->urlPathGenerator
+            ]
+        );
+    }
+
+    public function testGenerateForAllStores()
+    {
+        $initializesStores = [0];
+        $cmsPageId = 1;
+        $cmsPage = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $cmsPage->expects($this->any())->method('getStores')->willReturn($initializesStores);
+        $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
+            ->setMethods(['getStoreId'])
+            ->getMockForAbstractClass();
+        $this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]);
+        $store->expects($this->any())->method('getStoreId')->willReturn($initializesStores[0]);
+        $urlRewrite = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class)
+            ->getMockForAbstractClass();
+        $this->urlRewriteFactory->expects($this->any())->method('create')->willReturn($urlRewrite);
+        $cmsPage->expects($this->any())->method('getId')->willReturn($cmsPageId);
+        $cmsPage->expects($this->any())->method('getIdentifier')->willReturn('request_path');
+        $this->urlPathGenerator->expects($this->any())->method('getCanonicalUrlPath')->with($cmsPage)
+            ->willReturn('cms/page/view/page_id/' . $cmsPageId);
+
+        $urls = $this->urlRewriteGenerator->generate($cmsPage);
+        $this->assertEquals($initializesStores[0], $urls[0]->getStoreId());
+        $this->assertFalse(isset($urls[1]));
+    }
+
+    public function testGenerateForSpecificStores()
+    {
+        $initializesStores = [1, 2];
+        $cmsPageId = 1;
+        $cmsPage = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $cmsPage->expects($this->any())->method('getStores')->willReturn($initializesStores);
+        $firstStore = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
+            ->setMethods(['getStoreId'])
+            ->getMockForAbstractClass();
+        $secondStore = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
+            ->setMethods(['getStoreId'])
+            ->getMockForAbstractClass();
+        $this->storeManager->expects($this->any())->method('getStores')->willReturn(
+            [
+                1 => $firstStore,
+                2 => $secondStore
+            ]
+        );
+        $firstStore->expects($this->any())->method('getStoreId')->willReturn($initializesStores[0]);
+        $secondStore->expects($this->any())->method('getStoreId')->willReturn($initializesStores[1]);
+
+        $urlRewriteFirst = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class)
+            ->getMockForAbstractClass();
+        $urlRewriteSecond = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class)
+            ->getMockForAbstractClass();
+        $this->urlRewriteFactory->expects($this->at(0))->method('create')->willReturn($urlRewriteFirst);
+        $this->urlRewriteFactory->expects($this->at(1))->method('create')->willReturn($urlRewriteSecond);
+
+        $cmsPage->expects($this->any())->method('getId')->willReturn($cmsPageId);
+        $cmsPage->expects($this->any())->method('getIdentifier')->willReturn('request_path');
+        $this->urlPathGenerator->expects($this->any())->method('getCanonicalUrlPath')->with($cmsPage)
+            ->willReturn('cms/page/view/page_id/' . $cmsPageId);
+        $urls = $this->urlRewriteGenerator->generate($cmsPage);
+        $this->assertEquals(
+            [
+                $initializesStores[0],
+                $initializesStores[1]
+            ],
+            [
+                $urls[0]->getStoreId(),
+                $urls[1]->getStoreId(),
+            ]
+        );
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml
index e75831e28cf166290fbb042d6e09705a67442b82..75967a670279fba002fbce666b9f9d440669724b 100644
--- a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml
+++ b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml
@@ -35,7 +35,8 @@ $_attributes = $block->decorateArray($block->getAllowAttributes());
             "#product_addtocart_form": {
                 "configurable": {
                     "spConfig": <?php /* @escapeNotVerified */ echo $block->getJsonConfig() ?>,
-                    "onlyMainImg": <?php /* @escapeNotVerified */ echo $block->getVar('change_only_base_image', 'Magento_ConfigurableProduct') ?: 'false'; ?>
+                    "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy',
+                        'Magento_ConfigurableProduct') ?: 'replace'; ?>"
                 }
             }
         }
diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
index 7bea20e78620134fff06d4cd259fee029f4fe637..59b313bcb497ddb64e1fdca9bc480afdbbb2f1d9 100644
--- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
+++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
@@ -29,7 +29,16 @@ define([
             mediaGallerySelector: '[data-gallery-role=gallery-placeholder]',
             mediaGalleryInitial: null,
             slyOldPriceSelector: '.sly-old-price',
-            onlyMainImg: false
+
+            /**
+             * Defines the mechanism of how images of a gallery should be
+             * updated when user switches between configurations of a product.
+             *
+             * As for now value of this option can be either 'replace' or 'prepend'.
+             *
+             * @type {String}
+             */
+            gallerySwitchStrategy: 'replace'
         },
 
         /**
@@ -85,10 +94,10 @@ define([
 
             this.inputSimpleProduct = this.element.find(options.selectSimpleProduct);
 
-            gallery.on('gallery:loaded', function () {
-                var galleryObject = gallery.data('gallery');
-                options.mediaGalleryInitial = galleryObject.returnCurrentImages();
-            });
+            gallery.data('gallery') ?
+                this._onGalleryLoaded(gallery) :
+                gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery));
+
         },
 
         /**
@@ -259,46 +268,33 @@ define([
          */
         _changeProductImage: function () {
             var images,
-                initialImages = $.extend(true, [], this.options.mediaGalleryInitial),
+                initialImages = this.options.mediaGalleryInitial,
                 galleryObject = $(this.options.mediaGallerySelector).data('gallery');
 
-            if (this.options.spConfig.images[this.simpleProduct]) {
-                images = $.extend(true, [], this.options.spConfig.images[this.simpleProduct]);
+            if (!galleryObject) {
+                return;
             }
 
-            function updateGallery(imagesArr) {
-                var imgToUpdate,
-                    mainImg;
+            images = this.options.spConfig.images[this.simpleProduct];
 
-                mainImg = imagesArr.filter(function (img) {
-                    return img.isMain;
-                });
+            if (images) {
+                if (this.options.gallerySwitchStrategy === 'prepend') {
+                    images = images.concat(initialImages);
+                }
 
-                imgToUpdate = mainImg.length ? mainImg[0] : imagesArr[0];
-                galleryObject.updateDataByIndex(0, imgToUpdate);
-                galleryObject.seek(1);
-            }
+                images = $.extend(true, [], images);
 
-            if (galleryObject) {
-                if (images) {
-                    images.map(function (img) {
-                        img.type = 'image';
-                    });
+                images.forEach(function (img) {
+                    img.type = 'image';
+                });
 
-                    if (this.options.onlyMainImg) {
-                        updateGallery(images);
-                    } else {
-                        galleryObject.updateData(images)
-                    }
-                } else {
-                    if (this.options.onlyMainImg) {
-                        updateGallery(initialImages);
-                    } else {
-                        galleryObject.updateData(this.options.mediaGalleryInitial);
-                        $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
-                    }
-                }
+                galleryObject.updateData(images);
+            } else {
+                galleryObject.updateData(initialImages);
+                $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
             }
+
+            galleryObject.first();
         },
 
         /**
@@ -506,8 +502,18 @@ define([
             } else {
                 $(this.options.slyOldPriceSelector).hide();
             }
-        }
+        },
 
+        /**
+         * Callback which fired after gallery gets initialized.
+         *
+         * @param {HTMLElement} element - DOM element associated with gallery.
+         */
+        _onGalleryLoaded: function (element) {
+            var galleryObject = element.data('gallery');
+
+            this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();
+        }
     });
 
     return $.mage.configurable;
diff --git a/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php b/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
index d079c962cad3716d557feb593a10d246433bdd66..602c5db5c226e78ad2320e5ff6e2f0525ed32864 100644
--- a/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
+++ b/app/code/Magento/Cron/Test/Unit/Observer/ProcessCronQueueObserverTest.php
@@ -600,74 +600,74 @@ class ProcessCronQueueObserverTest extends \PHPUnit_Framework_TestCase
     public function testDispatchGenerate()
     {
         $jobConfig = [
-            'test_group' => [
-                'default' => [
-                    'test_job1' => [
-                        'instance' => 'CronJob',
-                        'method' => 'execute',
-                    ],
+            'default' => [
+                'test_job1' => [
+                    'instance' => 'CronJob',
+                    'method' => 'execute',
                 ],
             ],
         ];
 
-        $this->_config->expects($this->at(0))->method('getJobs')->will($this->returnValue($jobConfig));
         $jobs = [
-            'test_group' => [
-                'default' => [
-                    'job1' => ['config_path' => 'test/path'],
-                    'job2' => ['schedule' => ''],
-                    'job3' => ['schedule' => '* * * * *'],
-                ],
+            'default' => [
+                'job1' => ['config_path' => 'test/path'],
+                'job2' => ['schedule' => ''],
+                'job3' => ['schedule' => '* * * * *'],
             ],
         ];
-        $this->_config->expects($this->at(1))->method('getJobs')->will($this->returnValue($jobs));
-        $this->_request->expects($this->any())->method('getParam')->will($this->returnValue('test_group'));
+        $this->_config->expects($this->at(0))->method('getJobs')->willReturn($jobConfig);
+        $this->_config->expects($this->at(1))->method('getJobs')->willReturn($jobs);
+        $this->_request->expects($this->any())->method('getParam')->willReturn('default');
         $this->_cache->expects(
             $this->at(0)
         )->method(
             'load'
         )->with(
-            $this->equalTo(ProcessCronQueueObserver::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT . 'test_group')
-        )->will(
-            $this->returnValue(time() - 10000000)
-        );
+            $this->equalTo(ProcessCronQueueObserver::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT . 'default')
+        )->willReturn(time() - 10000000);
         $this->_cache->expects(
             $this->at(2)
         )->method(
             'load'
         )->with(
-            $this->equalTo(ProcessCronQueueObserver::CACHE_KEY_LAST_HISTORY_CLEANUP_AT . 'test_group')
-        )->will(
-            $this->returnValue(time() + 10000000)
-        );
+            $this->equalTo(ProcessCronQueueObserver::CACHE_KEY_LAST_HISTORY_CLEANUP_AT . 'default')
+        )->willReturn(time() + 10000000);
 
-        $this->_scopeConfig->expects($this->at(0))->method('getValue')->will($this->returnValue(0));
+        $this->_scopeConfig->expects($this->any())->method('getValue')->willReturnMap(
+            [
+                [
+                    'system/cron/default/schedule_generate_every',
+                    \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                    null,
+                    0
+                ],
+                [
+                    'system/cron/default/schedule_ahead_for',
+                    \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                    null,
+                    2
+                ]
+            ]
+        );
 
-        $scheduleMethods = ['getJobCode', 'getScheduledAt', 'trySchedule', 'unsScheduleId', 'save', '__wakeup'];
         $schedule = $this->getMockBuilder(
             \Magento\Cron\Model\Schedule::class
         )->setMethods(
-            $scheduleMethods
+            ['getJobCode', 'save', 'getScheduledAt', 'unsScheduleId', 'trySchedule', 'getCollection']
         )->disableOriginalConstructor()->getMock();
-        $schedule->expects($this->any())->method('getJobCode')->will($this->returnValue('job_code1'));
-        $schedule->expects($this->once())->method('getScheduledAt')->will($this->returnValue('* * * * *'));
-        $schedule->expects($this->any())->method('unsScheduleId')->will($this->returnSelf());
-        $schedule->expects($this->any())->method('trySchedule')->will($this->returnSelf());
+        $schedule->expects($this->any())->method('getJobCode')->willReturn('job_code1');
+        $schedule->expects($this->once())->method('getScheduledAt')->willReturn('* * * * *');
+        $schedule->expects($this->any())->method('unsScheduleId')->willReturnSelf();
+        $schedule->expects($this->any())->method('trySchedule')->willReturnSelf();
+        $schedule->expects($this->any())->method('getCollection')->willReturn($this->_collection);
+        $schedule->expects($this->exactly(1))->method('save')->willReturnSelf();
 
         $this->_collection->addItem(new \Magento\Framework\DataObject());
         $this->_collection->addItem($schedule);
 
         $this->_cache->expects($this->any())->method('save');
 
-        $scheduleMock = $this->getMockBuilder(
-            \Magento\Cron\Model\Schedule::class
-        )->disableOriginalConstructor()->setMethods(
-            ['getCollection', '__wakeup']
-        )->getMock();
-        $scheduleMock->expects($this->any())->method('getCollection')->will($this->returnValue($this->_collection));
-        $this->_scheduleFactory->expects($this->any())->method('create')->will($this->returnValue($scheduleMock));
-
-        $this->_scheduleFactory->expects($this->any())->method('create')->will($this->returnValue($schedule));
+        $this->_scheduleFactory->expects($this->any())->method('create')->willReturn($schedule);
 
         $this->_observer->execute($this->observer);
     }
diff --git a/app/code/Magento/Customer/Model/Account/Redirect.php b/app/code/Magento/Customer/Model/Account/Redirect.php
index ac03bd02553c762535ba575a7ca1ad398afb606a..5a1470959b60d37758bc698cdc2abaeadd3aea25 100644
--- a/app/code/Magento/Customer/Model/Account/Redirect.php
+++ b/app/code/Magento/Customer/Model/Account/Redirect.php
@@ -9,6 +9,7 @@ use Magento\Customer\Model\Session;
 use Magento\Customer\Model\Url as CustomerUrl;
 use Magento\Framework\App\RequestInterface;
 use Magento\Framework\Controller\ResultFactory;
+use Magento\Framework\Url\HostChecker;
 use Magento\Framework\UrlInterface;
 use Magento\Store\Model\ScopeInterface;
 use Magento\Store\Model\StoreManagerInterface;
@@ -53,6 +54,7 @@ class Redirect
     protected $customerUrl;
 
     /**
+     * @deprecated
      * @var UrlInterface
      */
     protected $url;
@@ -67,6 +69,11 @@ class Redirect
      */
     protected $cookieManager;
 
+    /**
+     * @var HostChecker
+     */
+    private $hostChecker;
+
     /**
      * @param RequestInterface $request
      * @param Session $customerSession
@@ -76,6 +83,7 @@ class Redirect
      * @param DecoderInterface $urlDecoder
      * @param CustomerUrl $customerUrl
      * @param ResultFactory $resultFactory
+     * @param HostChecker|null $hostChecker
      */
     public function __construct(
         RequestInterface $request,
@@ -85,7 +93,8 @@ class Redirect
         UrlInterface $url,
         DecoderInterface $urlDecoder,
         CustomerUrl $customerUrl,
-        ResultFactory $resultFactory
+        ResultFactory $resultFactory,
+        HostChecker $hostChecker = null
     ) {
         $this->request = $request;
         $this->session = $customerSession;
@@ -95,6 +104,7 @@ class Redirect
         $this->urlDecoder = $urlDecoder;
         $this->customerUrl = $customerUrl;
         $this->resultFactory = $resultFactory;
+        $this->hostChecker = $hostChecker ?: ObjectManager::getInstance()->get(HostChecker::class);
     }
 
     /**
@@ -196,7 +206,7 @@ class Redirect
             $referer = $this->request->getParam(CustomerUrl::REFERER_QUERY_PARAM_NAME);
             if ($referer) {
                 $referer = $this->urlDecoder->decode($referer);
-                if ($this->url->isOwnOriginUrl()) {
+                if ($this->hostChecker->isOwnOrigin($referer)) {
                     $this->applyRedirect($referer);
                 }
             }
diff --git a/app/code/Magento/Customer/Model/Url.php b/app/code/Magento/Customer/Model/Url.php
index fa2ff49aafd3abfab8d69c21589b0c6366150424..470093717549a871fb3deb6d74690dcfc45f4f2b 100644
--- a/app/code/Magento/Customer/Model/Url.php
+++ b/app/code/Magento/Customer/Model/Url.php
@@ -56,25 +56,43 @@ class Url
      */
     protected $urlEncoder;
 
+    /**
+     * @var \Magento\Framework\Url\DecoderInterface
+     */
+    private $urlDecoder;
+
+    /**
+     * @var \Magento\Framework\Url\HostChecker
+     */
+    private $hostChecker;
+
     /**
      * @param Session $customerSession
      * @param ScopeConfigInterface $scopeConfig
      * @param RequestInterface $request
      * @param UrlInterface $urlBuilder
      * @param EncoderInterface $urlEncoder
+     * @param \Magento\Framework\Url\DecoderInterface|null $urlDecoder
+     * @param \Magento\Framework\Url\HostChecker|null $hostChecker
      */
     public function __construct(
         Session $customerSession,
         ScopeConfigInterface $scopeConfig,
         RequestInterface $request,
         UrlInterface $urlBuilder,
-        EncoderInterface $urlEncoder
+        EncoderInterface $urlEncoder,
+        \Magento\Framework\Url\DecoderInterface $urlDecoder = null,
+        \Magento\Framework\Url\HostChecker $hostChecker = null
     ) {
         $this->request = $request;
         $this->urlBuilder = $urlBuilder;
         $this->scopeConfig = $scopeConfig;
         $this->customerSession = $customerSession;
         $this->urlEncoder = $urlEncoder;
+        $this->urlDecoder = $urlDecoder ?: \Magento\Framework\App\ObjectManager::getInstance()
+            ->get(\Magento\Framework\Url\DecoderInterface::class);
+        $this->hostChecker = $hostChecker ?: \Magento\Framework\App\ObjectManager::getInstance()
+            ->get(\Magento\Framework\Url\HostChecker::class);
     }
 
     /**
@@ -95,7 +113,7 @@ class Url
     public function getLoginUrlParams()
     {
         $params = [];
-        $referer = $this->request->getParam(self::REFERER_QUERY_PARAM_NAME);
+        $referer = $this->getRequestReferrer();
         if (!$referer
             && !$this->scopeConfig->isSetFlag(
                 self::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD,
@@ -122,9 +140,10 @@ class Url
     public function getLoginPostUrl()
     {
         $params = [];
-        if ($this->request->getParam(self::REFERER_QUERY_PARAM_NAME)) {
+        $referer = $this->getRequestReferrer();
+        if ($referer) {
             $params = [
-                self::REFERER_QUERY_PARAM_NAME => $this->request->getParam(self::REFERER_QUERY_PARAM_NAME),
+                self::REFERER_QUERY_PARAM_NAME => $referer,
             ];
         }
         return $this->urlBuilder->getUrl('customer/account/loginPost', $params);
@@ -220,4 +239,16 @@ class Url
     {
         return $this->urlBuilder->getUrl('customer/account/confirmation', ['email' => $email]);
     }
+
+    /**
+     * @return mixed|null
+     */
+    private function getRequestReferrer()
+    {
+        $referer = $this->request->getParam(self::REFERER_QUERY_PARAM_NAME);
+        if ($referer && $this->hostChecker->isOwnOrigin($this->urlDecoder->decode($referer))) {
+            return $referer;
+        }
+        return null;
+    }
 }
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php b/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php
index 5d512bcc6bda1a4fc0f154447a9ec91d74b74228..47b7ea669de30ead287169d1170acbb2c18ae301 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php
@@ -13,6 +13,7 @@ namespace Magento\Customer\Test\Unit\Model\Account;
 use Magento\Customer\Model\Account\Redirect;
 use Magento\Customer\Model\Url as CustomerUrl;
 use Magento\Framework\Controller\ResultFactory;
+use Magento\Framework\Url\HostChecker;
 use Magento\Store\Model\ScopeInterface;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
@@ -81,6 +82,11 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
      */
     protected $resultFactory;
 
+    /**
+     * @var HostChecker | \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $hostChecker;
+
     protected function setUp()
     {
         $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class);
@@ -134,6 +140,10 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
+        $this->hostChecker = $this->getMockBuilder(HostChecker::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $objectManager = new ObjectManager($this);
         $this->model = $objectManager->getObject(
             \Magento\Customer\Model\Account\Redirect::class,
@@ -145,7 +155,8 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
                 'url'                   => $this->url,
                 'urlDecoder'            => $this->urlDecoder,
                 'customerUrl'           => $this->customerUrl,
-                'resultFactory'         => $this->resultFactory
+                'resultFactory'         => $this->resultFactory,
+                'hostChecker' => $this->hostChecker
             ]
         );
     }
@@ -254,6 +265,7 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
 
         $this->resultRedirect->expects($this->once())
             ->method('setUrl')
+            ->with($beforeAuthUrl)
             ->willReturnSelf();
 
         $this->resultFactory->expects($this->once())
@@ -286,6 +298,7 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
         return [
             // Loggend In, Redirect by Referer
             [1, 2, 'referer', 'base', '', '', 'account', '', '', '', true, false],
+            [1, 2, 'http://referer.com/', 'http://base.com/', '', '', 'account', '', '', 'dashboard', true, false],
             // Loggend In, Redirect by AfterAuthUrl
             [1, 2, 'referer', 'base', '', 'defined', 'account', '', '', '', true, true],
             // Not logged In, Redirect by LoginUrl
diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/billing/method/form.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/billing/method/form.phtml
index 6d09c4d7601890d4948c5d40fb801b51371bd57f..4a9af33449b61c80b709998cf663b5601df26447 100644
--- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/billing/method/form.phtml
+++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/billing/method/form.phtml
@@ -40,9 +40,7 @@
 </div>
     <script>
         require(["Magento_Sales/order/create/form"], function(){
-        <?php if($_methodsCount == 1):?>
-            order.switchPaymentMethod('<?php /* @escapeNotVerified */ echo $block->getSelectedMethodCode(); ?>');
-        <?php else: ?>
+        <?php if($_methodsCount != 1):?>
             order.setPaymentMethod('<?php /* @escapeNotVerified */ echo $block->getSelectedMethodCode(); ?>');
         <?php endif; ?>
         });
diff --git a/app/code/Magento/SampleData/Model/Dependency.php b/app/code/Magento/SampleData/Model/Dependency.php
index 09f0c16aac848cafa797441137c9bc62f90a37ce..2a1849364bd77e0c43d19876579b3e2e7129b7ac 100644
--- a/app/code/Magento/SampleData/Model/Dependency.php
+++ b/app/code/Magento/SampleData/Model/Dependency.php
@@ -88,6 +88,10 @@ class Dependency
         foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleDir) {
             $file = $moduleDir . '/composer.json';
 
+            if (!file_exists($file) || !is_readable($file)) {
+                continue;
+            }
+
             /** @var Package $package */
             $package = $this->getModuleComposerPackage($file);
             $suggest = json_decode(json_encode($package->get('suggest')), true);
diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
index 19419d4c0a7eef2db8fe472c80b99f9246828993..478d6da720a79bf38df1bcf0b24ed1cc51061b3c 100644
--- a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
+++ b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
@@ -16,8 +16,8 @@
                 "jsonSwatchConfig": <?php /* @escapeNotVerified */
                     echo $swatchOptions = $block->getJsonSwatchConfig(); ?>,
                 "mediaCallback": "<?php /* @escapeNotVerified */ echo $block->getMediaCallback() ?>",
-                "onlyMainImg": <?php /* @escapeNotVerified */ echo $block->getVar('change_only_base_image',
-                    'Magento_Swatches') ?: 'false'; ?>
+                "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy',
+                    'Magento_ConfigurableProduct') ?: 'replace'; ?>"
             }
         }
     }
diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
index 452c9b6f94d3c60e83bd35a5a932c6b5f8a9092f..8af48829df438261aaed469c88245fc50a349853 100644
--- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
+++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
@@ -243,8 +243,15 @@ define([
             // Cache for BaseProduct images. Needed when option unset
             mediaGalleryInitial: [{}],
 
-            //
-            onlyMainImg: false,
+            /**
+             * Defines the mechanism of how images of a gallery should be
+             * updated when user switches between configurations of a product.
+             *
+             * As for now value of this option can be either 'replace' or 'prepend'.
+             *
+             * @type {String}
+             */
+            gallerySwitchStrategy: 'replace',
 
             // whether swatches are rendered in product list or on product page
             inProductList: false
@@ -295,11 +302,9 @@ define([
                     this.element.parents('.product-item-info');
 
             if (isProductViewExist) {
-                gallery.on('gallery:loaded', function () {
-                    var galleryObject = gallery.data('gallery');
-
-                    options.mediaGalleryInitial = galleryObject.returnCurrentImages();
-                });
+                gallery.data('gallery') ?
+                    this._onGalleryLoaded(gallery) :
+                    gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery));
             } else {
                 options.mediaGalleryInitial = [{
                     'img': $main.find('.product-image-photo').attr('src')
@@ -1032,26 +1037,27 @@ define([
          */
         updateBaseImage: function (images, context, isProductViewExist) {
             var justAnImage = images[0],
-                updateImg,
-                imagesToUpdate,
+                initialImages = this.options.mediaGalleryInitial,
                 gallery = context.find(this.options.mediaGallerySelector).data('gallery'),
-                item;
+                imagesToUpdate,
+                isInitial;
 
             if (isProductViewExist) {
                 imagesToUpdate = images.length ? this._setImageType($.extend(true, [], images)) : [];
+                isInitial = _.isEqual(imagesToUpdate, initialImages);
 
-                if (this.options.onlyMainImg) {
-                    updateImg = imagesToUpdate.filter(function (img) {
-                        return img.isMain;
-                    });
-                    item = updateImg.length ? updateImg[0] : imagesToUpdate[0];
-                    gallery.updateDataByIndex(0, item);
+                if (this.options.gallerySwitchStrategy === 'prepend' && !isInitial) {
+                    imagesToUpdate = imagesToUpdate.concat(initialImages);
+                }
 
-                    gallery.seek(1);
-                } else {
-                    gallery.updateData(imagesToUpdate);
+                gallery.updateData(imagesToUpdate);
+
+                if (isInitial) {
                     $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
                 }
+
+                gallery.first();
+
             } else if (justAnImage && justAnImage.img) {
                 context.find('.product-image-photo').attr('src', justAnImage.img);
             }
@@ -1127,6 +1133,17 @@ define([
             }
 
             return selectedAttributes;
+        },
+
+        /**
+         * Callback which fired after gallery gets initialized.
+         *
+         * @param {HTMLElement} element - DOM element associated with a gallery.
+         */
+        _onGalleryLoaded: function (element) {
+            var galleryObject = element.data('gallery');
+
+            this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();
         }
     });
 
diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml
index e0fc864e000840ebe770a85f6a78506f0d962a2f..12a51ee065edba43c9afde39ce31c2aace3d00a8 100644
--- a/app/design/frontend/Magento/luma/etc/view.xml
+++ b/app/design/frontend/Magento/luma/etc/view.xml
@@ -253,10 +253,7 @@
     </vars>
 
     <vars module="Magento_ConfigurableProduct">
-        <var name="change_only_base_image">true</var>
-    </vars>
-    <vars module="Magento_Swatches">
-        <var name="change_only_base_image">true</var>
+        <var name="gallery_switch_strategy">prepend</var>
     </vars>
 
     <vars module="Js_Bundle">
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 a341341cf7c1dfd873ed552c202a2ece65c4ef6c..fcf6e2ac0c4d0ece60aa742dfcb438456a0d9bd5 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
@@ -212,5 +212,21 @@
                 <item name="inherit" xsi:type="string">1</item>
             </field>
         </dataset>
+        <dataset name="enable_single_store_mode">
+            <field name="general/single_store_mode/enabled" 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_single_store_mode_rollback">
+            <field name="general/single_store_mode/enabled" 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/Cms/Test/Constraint/AssertCmsPageFormSingleStoreMode.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPageFormSingleStoreMode.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f96b2e3944751082b3371393d976e469bc7b81a
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPageFormSingleStoreMode.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Cms\Test\Constraint;
+
+use Magento\Cms\Test\Fixture\CmsPage;
+use Magento\Cms\Test\Page\Adminhtml\CmsPageIndex;
+use Magento\Cms\Test\Page\Adminhtml\CmsPageNew;
+
+/**
+ * Assert that displayed CMS page data on edit page equals passed from fixture.
+ */
+class AssertCmsPageFormSingleStoreMode extends AssertCmsPageForm
+{
+    /**
+     * Assert that displayed CMS page data on edit page equals passed from fixture with enabled single store mode.
+     *
+     * @param CmsPage $cms
+     * @param CmsPageIndex $cmsIndex
+     * @param CmsPageNew $cmsPageNew
+     * @return void
+     */
+    public function processAssert(
+        CmsPage $cms,
+        CmsPageIndex $cmsIndex,
+        CmsPageNew $cmsPageNew
+    ) {
+        $cmsIndex->open();
+        $filter = ['title' => $cms->getTitle()];
+        $cmsIndex->getCmsPageGridBlock()->searchAndOpen($filter);
+
+        $cmsFormData = $cmsPageNew->getPageForm()->getData($cms);
+        $cmsFixtureData = $cms->getData();
+        $errors = $this->verifyData($cmsFixtureData, $cmsFormData);
+        \PHPUnit_Framework_Assert::assertEmpty($errors, $errors);
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.php b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.php
index 250c55ff00d444635f08c2ea4a5f31d8b225fe10..4f02e7c4caf245528fab63d09d959ff3b8981779 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.php
@@ -6,6 +6,7 @@
 
 namespace Magento\Cms\Test\TestCase;
 
+use Magento\Config\Test\Fixture\ConfigData;
 use Magento\Cms\Test\Fixture\CmsPage as CmsPageFixture;
 use Magento\Cms\Test\Page\Adminhtml\CmsPageIndex;
 use Magento\Cms\Test\Page\Adminhtml\CmsPageNew;
@@ -53,6 +54,13 @@ class CreateCmsPageEntityTest extends Injectable
      */
     protected $fixtureFactory;
 
+    /**
+     * Configuration data.
+     *
+     * @var string
+     */
+    private $configData;
+
     /**
      * Inject pages.
      *
@@ -73,10 +81,18 @@ class CreateCmsPageEntityTest extends Injectable
      *
      * @param array $data
      * @param string $fixtureType
+     * @param string $configData
      * @return array
      */
-    public function test(array $data, $fixtureType)
+    public function test(array $data, $fixtureType, $configData = '')
     {
+        $this->configData = $configData;
+
+        // Preconditions
+        $this->objectManager->create(
+            \Magento\Config\Test\TestStep\SetupConfigurationStep::class,
+            ['configData' => $configData]
+        )->run();
         // Steps
         $cms = $this->fixtureFactory->createByCode($fixtureType, ['data' => $data]);
         $this->cmsIndex->open();
@@ -86,4 +102,19 @@ class CreateCmsPageEntityTest extends Injectable
 
         return ['cms' => $cms];
     }
+
+    /**
+     * Disable single store mode on config level.
+     *
+     * @return void
+     */
+    public function tearDown()
+    {
+        if ($this->configData) {
+            $this->objectManager->create(
+                \Magento\Config\Test\TestStep\SetupConfigurationStep::class,
+                ['configData' => 'enable_single_store_mode', 'rollback' => true]
+            )->run();
+        }
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml
index f8b6c8c3d9e06da90b122ba39d0c3f01eebc1978..fc024be14c699d1abb07629e701255ca0c6118f0 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml
@@ -67,5 +67,17 @@
             <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagePreview" />
             <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageOnFrontend" />
         </variation>
+        <variation name="CreateCmsPageEntityTestVariation6" summary="Create CMS page with single store mode" ticketId="MAGETWO-59654">
+            <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data>
+            <data name="configData" xsi:type="string">enable_single_store_mode</data>
+            <data name="fixtureType" xsi:type="string">cmsPage</data>
+            <data name="data/is_active" xsi:type="string">Yes</data>
+            <data name="data/title" xsi:type="string">NewCmsPage%isolation%</data>
+            <data name="data/identifier" xsi:type="string">identifier-%isolation%</data>
+            <data name="data/content/content" xsi:type="string">cms_page_text_content%isolation%</data>
+            <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageSuccessSaveMessage" />
+            <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageFormSingleStoreMode" />
+            <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageOnFrontend" />
+        </variation>
     </testCase>
 </config>
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
index 568450995f87ae0d0d11edd9dee5b03890e42129..042334a7619d85f31ed00623aff712086873c37a 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
@@ -41,6 +41,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * @magentoAppArea adminhtml
      * @magentoDataFixture Magento/Catalog/_files/indexer_catalog_category.php
      * @magentoDbIsolation enabled
      */
@@ -214,7 +215,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase
             \Magento\Catalog\Model\Category::class
         );
 
-        $result = $category->getCollection()->getItems();
+        $result = $category->getCollection()->addAttributeToSelect('name')->getItems();
         $result = array_slice($result, 2);
 
         return array_slice($result, 0, $count);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
index d8b2189e9f0d267d24d86ea77c67e84b2e2f73c9..c0e45410952717c783686e05b75d54eabdc6a295 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -568,52 +568,9 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase
      */
     public function testSaveMediaImage()
     {
-        $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-        $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
-            ->create(\Magento\Framework\Filesystem::class);
-        $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
-
-        $source = $this->objectManager->create(
-            \Magento\ImportExport\Model\Import\Source\Csv::class,
-            [
-                'file' => __DIR__ . '/_files/import_media.csv',
-                'directory' => $directory
-            ]
-        );
-        $this->_model->setParameters(
-            [
-                'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND,
-                'entity' => 'catalog_product',
-                'import_images_file_dir' => 'pub/media/import'
-            ]
-        );
-        $appParams = \Magento\TestFramework\Helper\Bootstrap::getInstance()
-            ->getBootstrap()
-            ->getApplication()
-            ->getInitParams()[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS];
-        $uploader = $this->_model->getUploader();
+        $this->importDataForMediaTest('import_media.csv');
+        $product = $this->getProductBySku('simple_new');
 
-        $destDir = $directory->getRelativePath($appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/catalog/product');
-        $tmpDir = $directory->getRelativePath($appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/import');
-
-        $directory->create($destDir);
-        $this->assertTrue($uploader->setDestDir($destDir));
-        $this->assertTrue($uploader->setTmpDir($tmpDir));
-        $errors = $this->_model->setSource(
-            $source
-        )->validateData();
-
-        $this->assertTrue($errors->getErrorsCount() == 0);
-        $this->_model->importData();
-        $this->assertTrue($this->_model->getErrorAggregator()->getErrorsCount() == 0);
-
-        $resource = $objectManager->get(\Magento\Catalog\Model\ResourceModel\Product::class);
-        $productId = $resource->getIdBySku('simple_new');
-
-        $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
-            \Magento\Catalog\Model\Product::class
-        );
-        $product->load($productId);
         $this->assertEquals('/m/a/magento_image.jpg', $product->getData('swatch_image'));
         $gallery = $product->getMediaGalleryImages();
         $this->assertInstanceOf(\Magento\Framework\Data\Collection::class, $gallery);
@@ -625,6 +582,25 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase
         $this->assertEquals('Image Label', $item->getLabel());
     }
 
+    /**
+     * Test that image labels updates after import
+     *
+     * @magentoDataFixture mediaImportImageFixture
+     * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+     */
+    public function testUpdateImageLabel()
+    {
+        $this->importDataForMediaTest('import_media_update_label.csv');
+        $product = $this->getProductBySku('simple');
+
+        $gallery = $product->getMediaGalleryImages();
+        $items = $gallery->getItems();
+        $this->assertCount(1, $items);
+        $item = array_pop($items);
+        $this->assertInstanceOf(\Magento\Framework\DataObject::class, $item);
+        $this->assertEquals('Updated Image Label', $item->getLabel());
+    }
+
     /**
      * Copy a fixture image into media import directory
      */
@@ -1432,6 +1408,68 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase
             $product2->getData('multiselect_attribute'));
     }
 
+    /**
+     * Import and check data from file
+     *
+     * @param string $fileName
+     */
+    private function importDataForMediaTest($fileName)
+    {
+        $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class);
+        $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
+
+        $source = $this->objectManager->create(
+            \Magento\ImportExport\Model\Import\Source\Csv::class,
+            [
+                'file' => __DIR__ . '/_files/' . $fileName,
+                'directory' => $directory
+            ]
+        );
+        $this->_model->setParameters(
+            [
+                'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND,
+                'entity' => 'catalog_product',
+                'import_images_file_dir' => 'pub/media/import'
+            ]
+        );
+        $appParams = \Magento\TestFramework\Helper\Bootstrap::getInstance()
+            ->getBootstrap()
+            ->getApplication()
+            ->getInitParams()[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS];
+        $uploader = $this->_model->getUploader();
+
+        $mediaPath = $appParams[DirectoryList::MEDIA][DirectoryList::PATH];
+        $destDir = $directory->getRelativePath($mediaPath . '/catalog/product');
+        $tmpDir = $directory->getRelativePath($mediaPath . '/import');
+
+        $directory->create($destDir);
+        $this->assertTrue($uploader->setDestDir($destDir));
+        $this->assertTrue($uploader->setTmpDir($tmpDir));
+        $errors = $this->_model->setSource(
+            $source
+        )->validateData();
+        $this->assertTrue($errors->getErrorsCount() == 0);
+
+        $this->_model->importData();
+        $this->assertTrue($this->_model->getErrorAggregator()->getErrorsCount() == 0);
+    }
+
+    /**
+     * Load product by given product sku
+     *
+     * @param string $sku
+     * @return \Magento\Catalog\Model\Product
+     */
+    private function getProductBySku($sku)
+    {
+        $resource = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Product::class);
+        $productId = $resource->getIdBySku($sku);
+        $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
+        $product->load($productId);
+
+        return $product;
+    }
+
     /**
      * @param array $row
      * @param string|null $behavior
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_update_label.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_update_label.csv
new file mode 100644
index 0000000000000000000000000000000000000000..4e62e28af7ff3c55232e5636334cb6023cbf2604
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_update_label.csv
@@ -0,0 +1,2 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus
+simple,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,,magento_image.jpg,,magento_image.jpg,,magento_image.jpg,,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,magento_image.jpg,Updated Image Label,,,,,,,,
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php
index 991d598401236c81a6be641b384e5a6587d55914..93fe95f83cbc98676bd14be0320b8517258ee7f3 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php
@@ -25,13 +25,6 @@ class CategoryUrlRewriteGeneratorTest extends \PHPUnit_Framework_TestCase
         $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
     }
 
-    public function tearDown()
-    {
-        $category = $this->objectManager->create(\Magento\Catalog\Model\Category::class);
-        $category->load(3);
-        $category->delete();
-    }
-
     /**
      * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories.php
      * @magentoDbIsolation enabled
@@ -96,6 +89,37 @@ class CategoryUrlRewriteGeneratorTest extends \PHPUnit_Framework_TestCase
         $this->assertResults($categoryExpectedResult, $actualResults);
     }
 
+    /**
+     * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories.php
+     * @magentoDbIsolation enabled
+     * @magentoAppIsolation enabled
+     * @param string $urlKey
+     * @dataProvider incorrectUrlRewritesDataProvider
+     */
+    public function testGenerateUrlRewritesWithIncorrectUrlKey($urlKey)
+    {
+        $this->setExpectedException(
+            \Magento\Framework\Exception\LocalizedException::class,
+            'Invalid URL key'
+        );
+        /** @var \Magento\Catalog\Api\CategoryRepositoryInterface $repository */
+        $repository = $this->objectManager->get(\Magento\Catalog\Api\CategoryRepositoryInterface::class);
+        $category = $repository->get(3);
+        $category->setUrlKey($urlKey);
+        $repository->save($category);
+    }
+
+    /**
+     * @return array
+     */
+    public function incorrectUrlRewritesDataProvider()
+    {
+        return [
+            ['#'],
+            ['//']
+        ];
+    }
+
     /**
      * @param array $filter
      * @return array
diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/View/Deployment/VersionTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/View/Deployment/VersionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5a313c318f9e9126ac84848a3b7ddf62e71d6585
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/App/View/Deployment/VersionTest.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\App\View\Deployment;
+
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\App\State;
+use Magento\Framework\App\View\Deployment\Version\Storage\File;
+use Magento\Framework\Filesystem\Directory\WriteInterface;
+
+class VersionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var File
+     */
+    private $fileStorage;
+
+    /**
+     * @var WriteInterface
+     */
+    private $directoryWrite;
+
+    /**
+     * @var string
+     */
+    private $fileName = 'deployed_version.txt';
+
+    public function setUp()
+    {
+        $this->fileStorage = ObjectManager::getInstance()->create(
+            File::class,
+            [
+                'directoryCode' => DirectoryList::STATIC_VIEW,
+                'fileName' => $this->fileName
+            ]
+        );
+        /** @var \Magento\TestFramework\App\Filesystem $filesystem */
+        $filesystem = ObjectManager::getInstance()->get(\Magento\TestFramework\App\Filesystem::class);
+        $this->directoryWrite = $filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
+        $this->removeDeployVersionFile();
+    }
+
+    /**
+     * @param string $mode
+     * @return Version
+     */
+    public function getVersionModel($mode)
+    {
+        $appState = ObjectManager::getInstance()->create(
+            State::class,
+            [
+                'mode' => $mode
+            ]
+        );
+        return ObjectManager::getInstance()->create(
+            Version::class,
+            [
+                'appState' => $appState
+            ]
+        );
+    }
+
+    protected function tearDown()
+    {
+        $this->removeDeployVersionFile();
+    }
+
+    private function removeDeployVersionFile()
+    {
+        if ($this->directoryWrite->isExist($this->fileName)) {
+            $this->directoryWrite->delete($this->fileName);
+        }
+    }
+
+    /**
+     * @expectedException \UnexpectedValueException
+     */
+    public function testGetValueInProductionModeWithoutVersion()
+    {
+        $this->assertFalse($this->directoryWrite->isExist($this->fileName));
+        $this->getVersionModel(State::MODE_PRODUCTION)->getValue();
+    }
+
+    public function testGetValueInDeveloperMode()
+    {
+        $this->assertFalse($this->directoryWrite->isExist($this->fileName));
+        $this->getVersionModel(State::MODE_DEVELOPER)->getValue();
+        $this->assertTrue($this->directoryWrite->isExist($this->fileName));
+    }
+
+    /**
+     * Assert that version is not regenerated on each request in developer mode
+     */
+    public function testGetValue()
+    {
+        $this->assertFalse($this->directoryWrite->isExist($this->fileName));
+        $versionModel = $this->getVersionModel(State::MODE_DEVELOPER);
+        $version = $versionModel->getValue();
+        $this->assertTrue($this->directoryWrite->isExist($this->fileName));
+        $this->assertEquals($version, $versionModel->getValue());
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/SampleData/Model/DependencyTest.php b/dev/tests/integration/testsuite/Magento/SampleData/Model/DependencyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ff9104cec879f48f48edc28f9f9860a599519d9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SampleData/Model/DependencyTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\SampleData\Model;
+
+use Magento\Framework\Composer\ComposerInformation;
+use Magento\Framework\Component\ComponentRegistrar;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Config\Composer\PackageFactory;
+
+class DependencyTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\SampleData\Model\Dependency
+     */
+    private $model;
+
+    /**
+     * @var ComposerInformation|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $composerInformationMock;
+
+    /**
+     * @var ComponentRegistrar|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $componentRegistrarMock;
+
+    protected function setUp()
+    {
+        $this->composerInformationMock = $this->getMockBuilder(ComposerInformation::class)
+            ->disableOriginalConstructor()
+            ->disableOriginalClone()
+            ->getMock();
+        $this->componentRegistrarMock = $this->getMockBuilder(ComponentRegistrar::class)
+            ->disableOriginalConstructor()
+            ->disableOriginalClone()
+            ->getMock();
+
+        $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+        $this->model = $objectManager->create(
+            \Magento\SampleData\Model\Dependency::class,
+            [
+                'composerInformation' => $this->composerInformationMock,
+                'filesystem' => $objectManager->get(Filesystem::class),
+                'packageFactory' => $objectManager->get(PackageFactory::class),
+                'componentRegistrar' => $this->componentRegistrarMock
+            ]
+        );
+    }
+
+    public function testGetSampleDataPackages()
+    {
+        $this->composerInformationMock->expects($this->once())
+            ->method('getSuggestedPackages')
+            ->willReturn([]);
+        $this->componentRegistrarMock->expects($this->once())
+            ->method('getPaths')
+            ->with(ComponentRegistrar::MODULE)
+            ->willReturn([
+                __DIR__ . '/../_files/Modules/FirstModule',
+                __DIR__ . '/../_files/Modules/SecondModule',
+                __DIR__ . '/../_files/Modules/ThirdModule',
+                __DIR__ . '/../_files/Modules/FourthModule'
+            ]);
+
+        $this->assertSame(
+            ['magento/module-first-sample-data' => '777.7.*'],
+            $this->model->getSampleDataPackages()
+        );
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/FirstModule/composer.json b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/FirstModule/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..d3f063976ea9180bc790e3597e771179f8f26563
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/FirstModule/composer.json
@@ -0,0 +1,9 @@
+{
+  "name": "magento/module-first",
+  "description": "N/A",
+  "suggest": {
+    "magento/module-first-sample-data": "Sample Data version:777.7.*"
+  },
+  "type": "magento2-module",
+  "version": "777.7.7"
+}
diff --git a/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/SecondModule/composer.json b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/SecondModule/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..b9e027b7bb6f5625dee2d8a64c26a9be6056c36b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/SecondModule/composer.json
@@ -0,0 +1,9 @@
+{
+  "name": "magento/module-second",
+  "description": "N/A",
+  "suggest": {
+    "magento/module-some-module": "Some Module:888.8.*"
+  },
+  "type": "magento2-module",
+  "version": "777.7.7"
+}
diff --git a/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/ThirdModule/composer.json b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/ThirdModule/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..2a0d642e8e282ecb2cec8ccb755a1dd3284f5a48
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/ThirdModule/composer.json
@@ -0,0 +1,6 @@
+{
+  "name": "magento/module-second",
+  "description": "N/A",
+  "type": "magento2-module",
+  "version": "777.7.7"
+}
diff --git a/dev/tools/grunt/configs/clean.js b/dev/tools/grunt/configs/clean.js
index 53bcd8a1d830ebda056e59c6ba592106abcdde72..e720b6c40c27e45e8ca27579727ee6dec67bd400 100644
--- a/dev/tools/grunt/configs/clean.js
+++ b/dev/tools/grunt/configs/clean.js
@@ -21,7 +21,8 @@ _.each(themes, function(theme, name) {
                     "<%= path.tmp %>/cache/**/*",
                     "<%= combo.autopath(\""+name+"\", path.pub ) %>**/*",
                     "<%= combo.autopath(\""+name+"\", path.tmpLess) %>**/*",
-                    "<%= combo.autopath(\""+name+"\", path.tmpSource) %>**/*"
+                    "<%= combo.autopath(\""+name+"\", path.tmpSource) %>**/*",
+                    "<%= path.deployedVersion %>"
                 ]
             }
         ]
@@ -56,7 +57,8 @@ var cleanOptions = {
                 "dot": true,
                 "src": [
                     "<%= path.pub %>frontend/**/*",
-                    "<%= path.pub %>adminhtml/**/*"
+                    "<%= path.pub %>adminhtml/**/*",
+                    "<%= path.deployedVersion %>"
                 ]
             }
         ]
@@ -73,7 +75,8 @@ var cleanOptions = {
                     "<%= path.pub %>frontend/**/*.less",
                     "<%= path.pub %>frontend/**/*.css",
                     "<%= path.pub %>adminhtml/**/*.less",
-                    "<%= path.pub %>adminhtml/**/*.css"
+                    "<%= path.pub %>adminhtml/**/*.css",
+                    "<%= path.deployedVersion %>"
                 ]
             }
         ]
@@ -102,7 +105,8 @@ var cleanOptions = {
                 "src": [
                     "<%= path.pub %>**/*.js",
                     "<%= path.pub %>**/*.html",
-                    "<%= path.pub %>_requirejs/**/*"
+                    "<%= path.pub %>_requirejs/**/*",
+                    "<%= path.deployedVersion %>"
                 ]
             }
         ]
diff --git a/dev/tools/grunt/configs/path.js b/dev/tools/grunt/configs/path.js
index 03621998c14a602c46eec390263aa8450dfc2657..e6a9cf71e81354b8e6b95e6d1623bbf799d831cf 100644
--- a/dev/tools/grunt/configs/path.js
+++ b/dev/tools/grunt/configs/path.js
@@ -13,6 +13,7 @@ module.exports = {
     tmpLess: 'var/view_preprocessed/less/',
     tmpSource: 'var/view_preprocessed/source/',
     tmp: 'var',
+    deployedVersion: 'pub/static/deployed_version.txt',
     css: {
         setup: 'setup/pub/styles',
         updater: '../magento2-updater/pub/css'
diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php
index b98998c55a40155ea785a9680c9b2e02beb85da9..e3de37bfc01f937618ae53ca897f16dc0b7fb188 100644
--- a/lib/internal/Magento/Framework/App/Http.php
+++ b/lib/internal/Magento/Framework/App/Http.php
@@ -241,8 +241,7 @@ class Http implements \Magento\Framework\AppInterface
                 . "because the Magento setup directory cannot be accessed. \n"
                 . 'You can install Magento using either the command line or you must restore access '
                 . 'to the following directory: ' . $setupInfo->getDir($projectRoot) . "\n";
-            $newMessage .= 'If you are using the sample nginx configuration, please go to '
-                . $this->_request->getScheme(). '://' . $this->_request->getHttpHost() . $setupInfo->getUrl();
+
             throw new \Exception($newMessage, 0, $exception);
         }
     }
diff --git a/lib/internal/Magento/Framework/App/SetupInfo.php b/lib/internal/Magento/Framework/App/SetupInfo.php
index b52daaac333e8c7f978aea2bcd156a68782bd6f1..731ec97ee7dd2d5dae8c2a372f454f11cf5755a7 100644
--- a/lib/internal/Magento/Framework/App/SetupInfo.php
+++ b/lib/internal/Magento/Framework/App/SetupInfo.php
@@ -147,6 +147,14 @@ class SetupInfo
     {
         $setupDir = $this->getDir($this->projectRoot);
         $isSubDir = false !== strpos($setupDir . '/', $this->docRoot . '/');
+        // Setup is not accessible from pub folder
+        $setupDir = rtrim($setupDir, '/');
+        $lastOccurrence = strrpos($setupDir, '/pub/setup');
+
+        if (false !== $lastOccurrence) {
+            $setupDir = substr_replace($setupDir, '/setup', $lastOccurrence, strlen('/pub/setup'));
+        }
+
         return $isSubDir && realpath($setupDir);
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php b/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
index 99ca759490f14d66d47988c70e34bf5d4e021ea2..61f0a34d8a0d78ebfe4d685d5f6ce5d30d1a057a 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
@@ -193,6 +193,13 @@ class SetupInfoTest extends \PHPUnit_Framework_TestCase
                 ],
                 true
             ],
+            'root within doc root + pub, existent sub-directory' => [
+                [
+                    'DOCUMENT_ROOT' => __DIR__ . '/_files/pub/',
+                    'SCRIPT_FILENAME' => __DIR__ . '/_files/pub/index.php',
+                ],
+                true
+            ],
         ];
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/Version/Storage/FileTest.php b/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/Version/Storage/FileTest.php
index 3981511ad47018c8847c8c9cddf889122e059d58..e7206bf5566075f4df04a977185b5f343ed42b8f 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/Version/Storage/FileTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/Version/Storage/FileTest.php
@@ -34,48 +34,15 @@ class FileTest extends \PHPUnit_Framework_TestCase
 
     public function testLoad()
     {
-        $this->directory
-            ->expects($this->once())
-            ->method('readFile')
+        $this->directory->expects($this->once())
+            ->method('isReadable')
             ->with('fixture_file.txt')
-            ->will($this->returnValue('123'));
-        $this->assertEquals('123', $this->object->load());
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage Exception to be propagated
-     */
-    public function testLoadExceptionPropagation()
-    {
-        $this->directory
-            ->expects($this->once())
+            ->willReturn(true);
+        $this->directory->expects($this->once())
             ->method('readFile')
             ->with('fixture_file.txt')
-            ->will($this->throwException(new \Exception('Exception to be propagated')));
-        $this->object->load();
-    }
-
-    /**
-     * @expectedException \UnexpectedValueException
-     * @expectedExceptionMessage Unable to retrieve deployment version of static files from the file system
-     */
-    public function testLoadExceptionWrapping()
-    {
-        $filesystemException = new \Magento\Framework\Exception\FileSystemException(
-            new \Magento\Framework\Phrase('File does not exist')
-        );
-        $this->directory
-            ->expects($this->once())
-            ->method('readFile')
-            ->with('fixture_file.txt')
-            ->will($this->throwException($filesystemException));
-        try {
-            $this->object->load();
-        } catch (\Exception $e) {
-            $this->assertSame($filesystemException, $e->getPrevious(), 'Wrapping of original exception is expected');
-            throw $e;
-        }
+            ->willReturn('123');
+        $this->assertEquals('123', $this->object->load());
     }
 
     public function testSave()
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/VersionTest.php b/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/VersionTest.php
index 187c043945d059992d6dc59a8713fc413b1a9752..8d804102f7a56038fded4118692931030593caa7 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/VersionTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/View/Deployment/VersionTest.php
@@ -6,7 +6,6 @@
 namespace Magento\Framework\App\Test\Unit\View\Deployment;
 
 use Magento\Framework\App\View\Deployment\Version;
-use Magento\Framework\Exception\FileSystemException;
 
 /**
  * Class VersionTest
@@ -45,17 +44,6 @@ class VersionTest extends \PHPUnit_Framework_TestCase
         $objectManager->setBackwardCompatibleProperty($this->object, 'logger', $this->loggerMock);
     }
 
-    public function testGetValueDeveloperMode()
-    {
-        $this->appStateMock
-            ->expects($this->once())
-            ->method('getMode')
-            ->will($this->returnValue(\Magento\Framework\App\State::MODE_DEVELOPER));
-        $this->versionStorageMock->expects($this->never())->method($this->anything());
-        $this->assertInternalType('integer', $this->object->getValue());
-        $this->object->getValue(); // Ensure computation occurs only once and result is cached in memory
-    }
-
     /**
      * @param string $appMode
      * @dataProvider getValueFromStorageDataProvider
@@ -81,106 +69,46 @@ class VersionTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
-    /**
-     * $param bool $isUnexpectedValueExceptionThrown
-     * $param bool $isFileSystemExceptionThrown
-     * @dataProvider getValueDefaultModeDataProvider
-     */
-    public function testGetValueDefaultMode(
-        $isUnexpectedValueExceptionThrown,
-        $isFileSystemExceptionThrown = null
-    ) {
-        $versionType = 'integer';
-        $this->appStateMock
-            ->expects($this->once())
-            ->method('getMode')
-            ->willReturn(\Magento\Framework\App\State::MODE_DEFAULT);
-        if ($isUnexpectedValueExceptionThrown) {
-            $storageException = new \UnexpectedValueException('Does not exist in the storage');
-            $this->versionStorageMock
-                ->expects($this->once())
-                ->method('load')
-                ->will($this->throwException($storageException));
-            $this->versionStorageMock->expects($this->once())
-                ->method('save')
-                ->with($this->isType($versionType));
-            if ($isFileSystemExceptionThrown) {
-                $fileSystemException = new FileSystemException(
-                    new \Magento\Framework\Phrase('Can not load static content version')
-                );
-                $this->versionStorageMock
-                    ->expects($this->once())
-                    ->method('save')
-                    ->will($this->throwException($fileSystemException));
-                $this->loggerMock->expects($this->once())
-                    ->method('critical')
-                    ->with('Can not save static content version.');
-            } else {
-                $this->loggerMock->expects($this->never())
-                    ->method('critical');
-            }
-        } else {
-            $this->versionStorageMock
-                ->expects($this->once())
-                ->method('load')
-                ->willReturn(1475779229);
-            $this->loggerMock->expects($this->never())
-                ->method('critical');
-        }
-        $this->assertInternalType($versionType, $this->object->getValue());
+    public function testGetValueInNonProductionMode()
+    {
+        $version = 123123123123;
+        $this->versionStorageMock->expects($this->once())
+            ->method('load')
+            ->willReturn($version);
+
+        $this->assertEquals($version, $this->object->getValue());
         $this->object->getValue();
     }
 
     /**
-     * @return array
+     * @expectedException \UnexpectedValueException
      */
-    public function getValueDefaultModeDataProvider()
+    public function testGetValueWithProductionModeAndException()
     {
-        return [
-            [false],
-            [true, false],
-            [true, true]
-        ];
-    }
-
-    /**
-     * @param bool $isUnexpectedValueExceptionThrown
-     * @dataProvider getValueProductionModeDataProvider
-     */
-    public function testGetValueProductionMode(
-        $isUnexpectedValueExceptionThrown
-    ) {
-        $this->appStateMock
-            ->expects($this->once())
+        $this->versionStorageMock->expects($this->once())
+            ->method('load')
+            ->willReturn(false);
+        $this->appStateMock->expects($this->once())
             ->method('getMode')
             ->willReturn(\Magento\Framework\App\State::MODE_PRODUCTION);
-        if ($isUnexpectedValueExceptionThrown) {
-            $storageException = new \UnexpectedValueException('Does not exist in the storage');
-            $this->versionStorageMock
-                ->expects($this->once())
-                ->method('load')
-                ->will($this->throwException($storageException));
-            $this->loggerMock->expects($this->once())
-                ->method('critical')
-                ->with('Can not load static content version.');
-        } else {
-            $this->versionStorageMock
-                ->expects($this->once())
-                ->method('load')
-                ->willReturn(1475779229);
-        }
-        $this->assertInternalType('integer', $this->object->getValue());
+        $this->loggerMock->expects($this->once())
+            ->method('critical')
+            ->with('Can not load static content version.');
+
         $this->object->getValue();
     }
 
-    /**
-     * @return array
-     */
-    public function getValueProductionModeDataProvider()
+    public function testGetValueWithProductionMode()
     {
-        return [
-            [false],
-            [true],
-        ];
+        $this->versionStorageMock->expects($this->once())
+            ->method('load')
+            ->willReturn(false);
+        $this->appStateMock->expects($this->once())
+            ->method('getMode')
+            ->willReturn(\Magento\Framework\App\State::MODE_DEFAULT);
+        $this->versionStorageMock->expects($this->once())
+            ->method('save');
+
+        $this->assertNotNull($this->object->getValue());
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/index.php b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a0cd37c68d37453c3e881693f25953bac966c1e
--- /dev/null
+++ b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/index.php
@@ -0,0 +1,5 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/_files/setup/index.php b/lib/internal/Magento/Framework/App/Test/Unit/_files/setup/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a0cd37c68d37453c3e881693f25953bac966c1e
--- /dev/null
+++ b/lib/internal/Magento/Framework/App/Test/Unit/_files/setup/index.php
@@ -0,0 +1,5 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
diff --git a/lib/internal/Magento/Framework/App/View/Deployment/Version.php b/lib/internal/Magento/Framework/App/View/Deployment/Version.php
index 7f6dc7fa9285ccc3987d84091bc9edb76d09f794..73d4a0c926cea88c5b2cfbd6db34888f3ef651fb 100644
--- a/lib/internal/Magento/Framework/App/View/Deployment/Version.php
+++ b/lib/internal/Magento/Framework/App/View/Deployment/Version.php
@@ -7,7 +7,6 @@
 namespace Magento\Framework\App\View\Deployment;
 
 use Psr\Log\LoggerInterface;
-use Magento\Framework\Exception\FileSystemException;
 
 /**
  * Deployment version of static files
@@ -67,23 +66,16 @@ class Version
      */
     protected function readValue($appMode)
     {
-        if ($appMode == \Magento\Framework\App\State::MODE_DEVELOPER) {
-            $result = $this->generateVersion();
-        } else {
-            try {
-                $result = $this->versionStorage->load();
-            } catch (\UnexpectedValueException $e) {
-                $result = $this->generateVersion();
-                if ($appMode == \Magento\Framework\App\State::MODE_DEFAULT) {
-                    try {
-                        $this->versionStorage->save($result);
-                    } catch (FileSystemException $e) {
-                        $this->getLogger()->critical('Can not save static content version.');
-                    }
-                } else {
-                    $this->getLogger()->critical('Can not load static content version.');
-                }
+        $result = $this->versionStorage->load();
+        if (!$result) {
+            if ($appMode == \Magento\Framework\App\State::MODE_PRODUCTION) {
+                $this->getLogger()->critical('Can not load static content version.');
+                throw new \UnexpectedValueException(
+                    "Unable to retrieve deployment version of static files from the file system."
+                );
             }
+            $result = $this->generateVersion();
+            $this->versionStorage->save($result);
         }
         return $result;
     }
diff --git a/lib/internal/Magento/Framework/App/View/Deployment/Version/Storage/File.php b/lib/internal/Magento/Framework/App/View/Deployment/Version/Storage/File.php
index 4f8813df774d7edc9a7490e31f447e6827dd2e38..0967cb634cbd706689bc0d54aa2c9a1b5b7a8b5e 100644
--- a/lib/internal/Magento/Framework/App/View/Deployment/Version/Storage/File.php
+++ b/lib/internal/Magento/Framework/App/View/Deployment/Version/Storage/File.php
@@ -40,15 +40,10 @@ class File implements \Magento\Framework\App\View\Deployment\Version\StorageInte
      */
     public function load()
     {
-        try {
+        if ($this->directory->isReadable($this->fileName)) {
             return $this->directory->readFile($this->fileName);
-        } catch (\Magento\Framework\Exception\FileSystemException $e) {
-            throw new \UnexpectedValueException(
-                'Unable to retrieve deployment version of static files from the file system.',
-                0,
-                $e
-            );
         }
+        return false;
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/Import.php b/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/Import.php
index 524ccc8c11fe311837d3390a8f9ec10ee890d375..260f6216b09bd1dd03fdee39f5102c1a717df32e 100644
--- a/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/Import.php
+++ b/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/Import.php
@@ -4,8 +4,6 @@
  * See COPYING.txt for license details.
  */
 
-// @codingStandardsIgnoreFile
-
 namespace Magento\Framework\Css\PreProcessor\Instruction;
 
 use Magento\Framework\View\Asset\LocalInterface;
@@ -22,7 +20,10 @@ class Import implements PreProcessorInterface
      * Pattern of @import instruction
      */
     const REPLACE_PATTERN =
-        '#@import\s+(\((?P<type>\w+)\)\s+)?[\'\"](?P<path>(?![/\\\]|\w:[/\\\])[^\"\']+)[\'\"]\s*?(?P<media>.*?);#';
+        '#@import(?!.*?\surl\(.*?)'
+        . '(?P<start>[\(\),\w\s]*?[\'\"])'
+        . '(?P<path>(?![/\\\]|\w*?:[/\\\])[^\"\']+)'
+        . '(?P<end>[\'\"][\s\w\(\)]*?);#';
 
     /**
      * @var \Magento\Framework\View\Asset\NotationResolver\Module
@@ -133,9 +134,9 @@ class Import implements PreProcessorInterface
         $matchedFileId = $this->fixFileExtension($matchedContent['path'], $contentType);
         $this->recordRelatedFile($matchedFileId, $asset);
         $resolvedPath = $this->notationResolver->convertModuleNotationToPath($asset, $matchedFileId);
-        $typeString = empty($matchedContent['type']) ? '' : '(' . $matchedContent['type'] . ') ';
-        $mediaString = empty($matchedContent['media']) ? '' : ' ' . trim($matchedContent['media']);
-        return "@import {$typeString}'{$resolvedPath}'{$mediaString};";
+        $start = $matchedContent['start'];
+        $end = $matchedContent['end'];
+        return "@import{$start}{$resolvedPath}{$end};";
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/ImportTest.php b/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/ImportTest.php
index 2441422bb182103100c3181bcfae4150423ef0b3..dc45ea75e0eb86f13771c7108a959376caad54f3 100644
--- a/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/ImportTest.php
+++ b/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/ImportTest.php
@@ -39,7 +39,7 @@ class ImportTest extends \PHPUnit_Framework_TestCase
     protected function setUp()
     {
 
-        $this->notationResolver = $this->getMock(
+        $this->notationResolver = $this->getMock(
             \Magento\Framework\View\Asset\NotationResolver\Module::class, [], [], '', false
         );
         $this->asset = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false);
@@ -63,7 +63,11 @@ class ImportTest extends \PHPUnit_Framework_TestCase
     public function testProcess($originalContent, $foundPath, $resolvedPath, $expectedContent)
     {
         $chain = new \Magento\Framework\View\Asset\PreProcessor\Chain($this->asset, $originalContent, 'less', 'path');
-        $this->notationResolver->expects($this->once())
+        $invoke =  $this->once();
+        if (preg_match('/^(http:|https:|\/+)/', $foundPath)) {
+            $invoke = $this->never();
+        }
+        $this->notationResolver->expects($invoke)
             ->method('convertModuleNotationToPath')
             ->with($this->asset, $foundPath)
             ->will($this->returnValue($resolvedPath));
@@ -78,50 +82,70 @@ class ImportTest extends \PHPUnit_Framework_TestCase
     public function processDataProvider()
     {
         return [
-            'non-modular notation' => [
-                '@import (type) "some/file.css" media;',
-                'some/file.css',
-                'some/file.css',
-                "@import (type) 'some/file.css' media;",
+            'non-modular notation, no extension' => [
+                '@import (type) \'some/file\' media;',
+                'some/file.less',
+                'some/file.less',
+                '@import (type) \'some/file.less\' media;',
             ],
             'modular, with extension' => [
                 '@import (type) "Magento_Module::something.css" media;',
                 'Magento_Module::something.css',
                 'Magento_Module/something.css',
-                "@import (type) 'Magento_Module/something.css' media;",
+                '@import (type) "Magento_Module/something.css" media;',
+            ],
+            'remote file import url()' => [
+                '@import (type) url("http://example.com/css/some.css") media;',
+                'http://example.com/css/some.css',
+                null,
+                '@import (type) url("http://example.com/css/some.css") media;',
+            ],
+            'invalid path' => [
+                '@import (type) url("/example.com/css/some.css") media;',
+                '/example.com/css/some.css',
+                null,
+                '@import (type) url("/example.com/css/some.css") media;',
             ],
             'modular, no extension' => [
                 '@import (type) "Magento_Module::something" media;',
                 'Magento_Module::something.less',
                 'Magento_Module/something.less',
-                "@import (type) 'Magento_Module/something.less' media;",
+                '@import (type) "Magento_Module/something.less" media;',
             ],
             'no type' => [
                 '@import "Magento_Module::something.css" media;',
                 'Magento_Module::something.css',
                 'Magento_Module/something.css',
-                "@import 'Magento_Module/something.css' media;",
+                '@import "Magento_Module/something.css" media;',
             ],
             'no media' => [
                 '@import (type) "Magento_Module::something.css";',
                 'Magento_Module::something.css',
                 'Magento_Module/something.css',
-                "@import (type) 'Magento_Module/something.css';",
+                '@import (type) "Magento_Module/something.css";',
+            ],
+            'with single line comment, replace' => [
+                '@import (type) "some/file" media;' . PHP_EOL
+                . '// @import (type) "unnecessary/file.css" media;',
+                'some/file.less',
+                'some/file.less',
+                '@import (type) "some/file.less" media;' . PHP_EOL,
             ],
-            'with single line comment' => [
-                '@import (type) "some/file.css" media;' . PHP_EOL
-                    . '// @import (type) "unnecessary/file.css" media;',
-                'some/file.css',
-                'some/file.css',
-                "@import (type) 'some/file.css' media;" . PHP_EOL,
+            'with single line comment, no replace' => [
+                '@import (type) "some/file.less" media;' . PHP_EOL
+                . '// @import (type) "unnecessary/file" media;',
+                'some/file.less',
+                'some/file.less',
+                '@import (type) "some/file.less" media;' . PHP_EOL
+                . '// @import (type) "unnecessary/file" media;',
             ],
             'with multi line comment' => [
-                '@import (type) "some/file.css" media;' . PHP_EOL
+                '@import (type) "some/file" media;' . PHP_EOL
                     . '/* @import (type) "unnecessary/file.css" media;' . PHP_EOL
                     . '@import (type) "another/unnecessary/file.css" media; */',
-                'some/file.css',
-                'some/file.css',
-                "@import (type) 'some/file.css' media;" . PHP_EOL,
+                'some/file.less',
+                'some/file.less',
+                '@import (type) "some/file.less" media;' . PHP_EOL,
             ],
         ];
     }
diff --git a/lib/internal/Magento/Framework/Test/Unit/ObjectManager/Config/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php
similarity index 98%
rename from lib/internal/Magento/Framework/Test/Unit/ObjectManager/Config/CompiledTest.php
rename to lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php
index 3dd9f5065a1421ec822d4a81377ea81a5c3f8f52..1fcf3176540db6279e3fec6754fb3a7a22b3cc59 100644
--- a/lib/internal/Magento/Framework/Test/Unit/ObjectManager/Config/CompiledTest.php
+++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php
@@ -3,7 +3,7 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-namespace Magento\Framework\Test\Unit\ObjectManager\Config;
+namespace Magento\Framework\ObjectManager\Test\Unit\Config;
 
 use Magento\Framework\ObjectManager\Config\Compiled as CompiledConfig;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
index 36fce179395b5463fc3cf522defc5075038fc5c1..939e9a39bea584adc34037fcb9a899c4d3fe3e68 100644
--- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
+++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
@@ -7,6 +7,7 @@
 // @codingStandardsIgnoreFile
 
 namespace Magento\Framework\Test\Unit;
+use Magento\Framework\Url\HostChecker;
 
 /**
  * Test class for Magento\Framework\Url
@@ -59,6 +60,11 @@ class UrlTest extends \PHPUnit_Framework_TestCase
      */
     protected $urlModifier;
 
+    /**
+     * @var HostChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $hostChecker;
+
     protected function setUp()
     {
         $this->routeParamsResolverMock = $this->getMock(
@@ -549,18 +555,17 @@ class UrlTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @param bool $result
-     * @param string $baseUrl
      * @param string $referrer
      * @dataProvider isOwnOriginUrlDataProvider
      */
-    public function testIsOwnOriginUrl($result, $baseUrl, $referrer)
+    public function testIsOwnOriginUrl($result, $referrer)
     {
         $requestMock = $this->getRequestMock();
-        $model = $this->getUrlModel(['scopeResolver' => $this->scopeResolverMock, 'request' => $requestMock]);
+        $this->hostChecker = $this->getMockBuilder(HostChecker::class)
+            ->disableOriginalConstructor()->getMock();
+        $this->hostChecker->expects($this->once())->method('isOwnOrigin')->with($referrer)->willReturn($result);
+        $model = $this->getUrlModel(['hostChecker' => $this->hostChecker, 'request' => $requestMock]);
 
-        $this->scopeMock->expects($this->any())->method('getBaseUrl')->will($this->returnValue($baseUrl));
-        $this->scopeResolverMock->expects($this->any())->method('getScopes')
-            ->will($this->returnValue([$this->scopeMock]));
         $requestMock->expects($this->once())->method('getServer')->with('HTTP_REFERER')
             ->will($this->returnValue($referrer));
 
@@ -570,8 +575,8 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     public function isOwnOriginUrlDataProvider()
     {
         return [
-            'is origin url' => [true, 'http://localhost/', 'http://localhost/'],
-            'is not origin url' => [false, 'http://localhost/', 'http://example.com/'],
+            'is origin url' => [true, 'http://localhost/'],
+            'is not origin url' => [false, 'http://example.com/'],
         ];
     }
 
diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php
index 7361fdb336dd8ecd9ce58507dda027f1731f1d58..30af3528b2baba392095ecb86f9e65eb78bfd8a1 100644
--- a/lib/internal/Magento/Framework/Url.php
+++ b/lib/internal/Magento/Framework/Url.php
@@ -8,6 +8,8 @@
 
 namespace Magento\Framework;
 
+use Magento\Framework\Url\HostChecker;
+
 /**
  * URL
  *
@@ -178,6 +180,11 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
      */
     private $escaper;
 
+    /**
+     * @var HostChecker
+     */
+    private $hostChecker;
+
     /**
      * @param \Magento\Framework\App\Route\ConfigInterface $routeConfig
      * @param \Magento\Framework\App\RequestInterface $request
@@ -191,6 +198,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
      * @param \Magento\Framework\Url\RouteParamsPreprocessorInterface $routeParamsPreprocessor
      * @param string $scopeType
      * @param array $data
+     * @param HostChecker|null $hostChecker
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -205,7 +213,8 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
         \Magento\Framework\Url\RouteParamsPreprocessorInterface $routeParamsPreprocessor,
         $scopeType,
-        array $data = []
+        array $data = [],
+        HostChecker $hostChecker = null
     ) {
         $this->_request = $request;
         $this->_routeConfig = $routeConfig;
@@ -218,6 +227,8 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
         $this->_scopeConfig = $scopeConfig;
         $this->routeParamsPreprocessor = $routeParamsPreprocessor;
         $this->_scopeType = $scopeType;
+        $this->hostChecker = $hostChecker ?: \Magento\Framework\App\ObjectManager::getInstance()
+            ->get(HostChecker::class);
         parent::__construct($data);
     }
 
@@ -1086,17 +1097,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
      */
     public function isOwnOriginUrl()
     {
-        $scopeDomains = [];
-        $referer = parse_url($this->_request->getServer('HTTP_REFERER'), PHP_URL_HOST);
-        foreach ($this->_scopeResolver->getScopes() as $scope) {
-            $scopeDomains[] = parse_url($scope->getBaseUrl(), PHP_URL_HOST);
-            $scopeDomains[] = parse_url($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true), PHP_URL_HOST);
-        }
-        $scopeDomains = array_unique($scopeDomains);
-        if (empty($referer) || in_array($referer, $scopeDomains)) {
-            return true;
-        }
-        return false;
+        return $this->hostChecker->isOwnOrigin($this->_request->getServer('HTTP_REFERER'));
     }
 
     /**
@@ -1163,7 +1164,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur
     private function getUrlModifier()
     {
         if ($this->urlModifier === null) {
-            $this->urlModifier = \Magento\Framework\App\ObjectManager::getInstance()->get(
+            $this->urlModifier = \Magento\Framework\App\ObjectManager::getInstance()->get(
                 \Magento\Framework\Url\ModifierInterface::class
             );
         }
diff --git a/lib/internal/Magento/Framework/Url/HostChecker.php b/lib/internal/Magento/Framework/Url/HostChecker.php
new file mode 100644
index 0000000000000000000000000000000000000000..32546595a040dee59dfd9de3dae2577c35bbf444
--- /dev/null
+++ b/lib/internal/Magento/Framework/Url/HostChecker.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Url;
+
+use Magento\Framework\UrlInterface;
+
+/**
+ * Class provides functionality for checks of a host name
+ */
+class HostChecker
+{
+    /**
+     * @var \Magento\Framework\Url\ScopeResolverInterface
+     */
+    private $scopeResolver;
+
+    /**
+     * @param ScopeResolverInterface $scopeResolver
+     */
+    public function __construct(ScopeResolverInterface $scopeResolver)
+    {
+        $this->scopeResolver = $scopeResolver;
+    }
+
+    /**
+     * Check if provided URL is one of the domain URLs assigned to scopes
+     *
+     * @param string $url
+     * @return bool
+     */
+    public function isOwnOrigin($url)
+    {
+        $scopeHostNames = [];
+        $hostName = parse_url($url, PHP_URL_HOST);
+        if (empty($hostName)) {
+            return true;
+        }
+        foreach ($this->scopeResolver->getScopes() as $scope) {
+            $scopeHostNames[] = parse_url($scope->getBaseUrl(), PHP_URL_HOST);
+            $scopeHostNames[] = parse_url($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true), PHP_URL_HOST);
+        }
+        $scopeHostNames = array_unique($scopeHostNames);
+        return in_array($hostName, $scopeHostNames);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Url/Test/Unit/HostCheckerTest.php b/lib/internal/Magento/Framework/Url/Test/Unit/HostCheckerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a6be097ac38ea19c2d1bdc2f8bac4cc12306f7b6
--- /dev/null
+++ b/lib/internal/Magento/Framework/Url/Test/Unit/HostCheckerTest.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Url\Test\Unit;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class HostCheckerTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var \Magento\Framework\Url\HostChecker */
+    private $object;
+
+    /** @var \Magento\Framework\Url\ScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject */
+    private $scopeResolver;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $this->scopeResolver = $this->getMockBuilder(
+            \Magento\Framework\Url\ScopeResolverInterface::class
+        )->getMock();
+
+        $objectManager = new ObjectManager($this);
+        $this->object = $objectManager->getObject(
+            \Magento\Framework\Url\HostChecker::class,
+            [
+                'scopeResolver' => $this->scopeResolver
+            ]
+        );
+    }
+
+    /**
+     * @dataProvider isOwnOriginDataProvider
+     * @param string $url
+     * @param boolean $result
+     */
+    public function testIsOwnOrigin($url, $result)
+    {
+        $scopes[0] = $this->getMockBuilder(\Magento\Framework\Url\ScopeInterface::class)->getMock();
+        $scopes[0]->expects($this->any())->method('getBaseUrl')->willReturn('http://www.example.com');
+        $scopes[1] = $this->getMockBuilder(\Magento\Framework\Url\ScopeInterface::class)->getMock();
+        $scopes[1]->expects($this->any())->method('getBaseUrl')->willReturn('https://www.example2.com');
+
+        $this->scopeResolver->expects($this->atLeastOnce())->method('getScopes')->willReturn($scopes);
+
+        $this->assertEquals($result, $this->object->isOwnOrigin($url));
+    }
+
+    /**
+     * @return array
+     */
+    public function isOwnOriginDataProvider()
+    {
+        return [
+            ['http://www.example.com/some/page/', true],
+            ['http://www.test.com/other/page/', false],
+        ];
+    }
+}
diff --git a/pub/media/.htaccess b/pub/media/.htaccess
index 865ebd31b528b8183593ee5e55894dedc53a46fd..0a3087c096319f5d9974d643d33317ee0a4c4af2 100644
--- a/pub/media/.htaccess
+++ b/pub/media/.htaccess
@@ -11,6 +11,10 @@ php_flag engine 0
 AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi
 Options -ExecCGI
 
+<FilesMatch ".+\.(ph(p[3457]?|t|tml)|[aj]sp|p[ly]|sh|cgi|shtml?|html?)$">
+SetHandler default-handler
+</FilesMatch>
+
 <IfModule mod_rewrite.c>
 
 ############################################
diff --git a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php
index bbd0a44254e89ce2e84d3bbe7e043ac80c76a435..ad2ad3d7b6969dcf241bd9f273ae4330d9fedcd2 100644
--- a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php
@@ -71,7 +71,7 @@ class UpgradeCommand extends AbstractSetupCommand
         $installer->installSchema();
         $installer->installDataFixtures();
         if (!$keepGenerated) {
-            $output->writeln('<info>Please re-run Magento compile command</info>');
+            $output->writeln('<info>Please re-run Magento compile command. Use the command "setup:di:compile"</info>');
         }
 
         return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php
index 6209df88bd5f06880fad8c645128af079d61b482..dcbdc876db607a010e543b76c62d5410a4ca0132 100644
--- a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php
@@ -11,7 +11,12 @@ use Magento\Framework\Console\Cli;
 
 class UpgradeCommandTest extends \PHPUnit_Framework_TestCase
 {
-    public function testExecute()
+    /**
+     * @param array $options
+     * @param string $expectedString
+     * @dataProvider executeDataProvider
+     */
+    public function testExecute($options = [], $expectedString = '')
     {
         $installerFactory = $this->getMock(\Magento\Setup\Model\InstallerFactory::class, [], [], '', false);
         $installer = $this->getMock(\Magento\Setup\Model\Installer::class, [], [], '', false);
@@ -20,6 +25,25 @@ class UpgradeCommandTest extends \PHPUnit_Framework_TestCase
         $installer->expects($this->at(2))->method('installDataFixtures');
         $installerFactory->expects($this->once())->method('create')->willReturn($installer);
         $commandTester = new CommandTester(new UpgradeCommand($installerFactory));
-        $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->execute([]));
+        $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->execute($options));
+        $this->assertEquals($expectedString, $commandTester->getDisplay());
+    }
+
+    /**
+     * @return array
+     */
+    public function executeDataProvider()
+    {
+        return [
+            [
+                'options' => [],
+                'expectedString' => 'Please re-run Magento compile command. Use the command "setup:di:compile"'
+                    . PHP_EOL
+            ],
+            [
+                'options' => ['--keep-generated' => true],
+                'expectedString' => ''
+            ],
+        ];
     }
 }