From ea1a2b7f2fc7fdb81ea1d73cbb62b5c43d9845db Mon Sep 17 00:00:00 2001
From: mage2-team <mage2-team@magento.com>
Date: Fri, 20 Jun 2014 10:41:34 -0700
Subject: [PATCH] 2.0.0.0-dev83 * Created the Service API for the
 Magento_Catalog Module:    * Product Attribute Media API    * Product Group
 Price API * Tax calculation updates:   * Fixed tax calculation rounding
 issues which appeared when a discount was applied   * Fixed extra penny issue
 which appeared when exact tax amount ended with 0.5 cent   * Fixed tax
 calculation issues which appeared when a customer tax rate was different from
 the store tax rate   * Fixed price inconsistencies between catalog and
 shopping cart   * Added support for maintaining consistent prices including
 tax for customers with different tax rates   * Added support for applying tax
 rules with different priorities to be applied to subtotal only   * Added
 support for tax rounding at individual tax rate * Porting Tax Features from
 Magento 1.x:   * Price consistency UX and algorithm   * Canadian provincial
 sales taxes   * Fixed issues with bundle product price inconsistency across
 the system   * Added warnings if invalid tax configuration is created in the
 Admin panel   * Fixed issues with regards to hidden tax * Fixed bugs:   *
 Fixed an issue where grouped price was not applied for grouped products   *
 Fixed an issue where a fatal error occurred when opening a grouped product
 page without assigned products on the frontend   * Fixed an issue where it
 was possible to apply an inactive discount coupon   * Fixed an issue where
 the linked products information was lost when exporting products   * Fixed
 non-informative error messages for "Attribute Group Service"   * Fixed the
 invalid default value of the "apply_after_discount" tax setting   * Fixed an
 issue where the integration tests coverage whitelist was broken   * Fixed
 Admin panel UI issues: grids, headers and footers * Added the following
 functional tests:   * Create Product Url Rewrite   * Delete Catalog Price
 Rule   * Delete Category Url Rewrite   * Delete CMS Page Rewrite   * Delete
 Product Rating   * Delete Sales Rule   * Delete Tax Rate   * Update Catalog
 Price Rule   * Update Shopping Cart

---
 .travis.yml                                   |   2 +-
 CHANGELOG.md                                  |  39 ++
 .../view/adminhtml/layout/default.xml         |   2 +-
 .../Catalog/Product/View/Type/Bundle.php      |  31 ++
 .../Bundle/Pricing/Adjustment/Calculator.php  |  33 +-
 .../catalog/product/view/type/bundle.phtml    |   1 +
 .../Bundle/view/frontend/web/bundle.js        |  18 +-
 .../Magento/Catalog/Block/Product/View.php    |   2 +-
 .../Backend/Groupprice/AbstractGroupprice.php |   5 +
 .../Model/Product/Attribute/Backend/Media.php |   1 +
 .../Catalog/Model/Product/PriceModifier.php   | 109 ++++
 .../Catalog/Model/ProductRepository.php       |  73 +++
 .../Catalog/Pricing/Price/GroupPrice.php      |  15 +-
 .../Service/V1/Data/Product/GroupPrice.php    |  53 ++
 .../V1/Data/Product/GroupPriceBuilder.php     |  54 ++
 .../Service/V1/Data/Product/TierPrice.php     |  54 ++
 .../V1/Data/Product/TierPriceBuilder.php      |  54 ++
 .../Attribute/Media/Data/GalleryEntry.php     |  98 ++++
 .../Media/Data/GalleryEntryBuilder.php        |  97 ++++
 .../Media/Data/GalleryEntryContent.php        |  65 +++
 .../Media/Data/GalleryEntryContentBuilder.php |  64 +++
 .../Data/GalleryEntryContentValidator.php     | 108 ++++
 .../Attribute/Media/Data/MediaImage.php       |  78 +++
 .../Media/Data/MediaImageBuilder.php          |  74 +++
 .../Attribute/Media/GalleryEntryResolver.php  |  75 +++
 .../Product/Attribute/Media/ReadService.php   | 242 +++++++++
 .../Attribute/Media/ReadServiceInterface.php  |  54 ++
 .../Product/Attribute/Media/WriteService.php  | 225 +++++++++
 .../Attribute/Media/WriteServiceInterface.php |  69 +++
 .../V1/Product/AttributeGroup/ReadService.php |  16 +-
 .../AttributeGroup/ReadServiceInterface.php   |   1 +
 .../Product/AttributeGroup/WriteService.php   |  51 +-
 .../AttributeGroup/WriteServiceInterface.php  |   8 +-
 .../Service/V1/Product/GroupPriceService.php  | 175 +++++++
 .../V1/Product/GroupPriceServiceInterface.php |  59 +++
 .../Service/V1/Product/TierPriceService.php   | 192 +++++++
 .../V1/Product/TierPriceServiceInterface.php  |  62 +++
 app/code/Magento/Catalog/etc/di.xml           |   4 +
 app/code/Magento/Catalog/etc/webapi.xml       |  72 +++
 .../Model/Resource/Attribute/Collection.php   |  20 +-
 .../templates/product/price/final_price.phtml |  19 +-
 .../templates/product/view/type/grouped.phtml |   4 -
 .../Sales/Model/Quote/Item/AbstractItem.php   |   2 +-
 .../Magento/SalesRule/Model/Validator.php     |   3 +-
 .../Tax/Block/Adminhtml/Rule/Edit/Form.php    |  14 +
 .../Magento/Tax/Block/Sales/Order/Tax.php     |  24 +-
 .../Magento/Tax/Controller/Adminhtml/Rule.php |  13 +
 .../Magento/Tax/Controller/Adminhtml/Tax.php  |  20 +
 app/code/Magento/Tax/Helper/Data.php          |  37 ++
 app/code/Magento/Tax/Model/Config.php         |  71 ++-
 .../Magento/Tax/Model/Config/Notification.php |  83 +++
 .../Tax/Model/Sales/Total/Quote/Tax.php       |   1 -
 .../Model/System/Message/Notifications.php    | 337 +++++++++++++
 app/code/Magento/Tax/Pricing/Adjustment.php   |   4 +-
 app/code/Magento/Tax/etc/adminhtml/di.xml     |  34 ++
 app/code/Magento/Tax/etc/adminhtml/system.xml |  16 +
 app/code/Magento/Tax/etc/config.xml           |   5 +-
 app/code/Magento/Tax/etc/module.xml           |   1 +
 .../view/adminhtml/layout/tax_rule_block.xml  |   7 +
 app/code/Magento/Weee/Model/Tax.php           |   2 +-
 .../web/css/source/module.less                |   2 +
 .../Magento/backend/web/css/admin.less        |  10 +-
 .../Magento/backend/web/css/source/table.less |  20 +-
 .../adminhtml/Magento/backend/web/js/theme.js |  14 +-
 .../Element/MultiselectlistElement.php        |  18 +
 .../Backend/Test/Block/FormPageActions.php    |   1 -
 .../Catalog/Test/Block/Product/Price.php      |  16 +-
 .../Test/Constraint/AssertProductInCart.php   |   2 +-
 .../Constraint/AssertProductInCategory.php    |   3 +-
 .../Fixture/CatalogProductSimple/Price.php    |  42 +-
 .../app/Magento/Checkout/Test/Block/Cart.php  |  92 +++-
 .../Checkout/Test/Block/Cart/Sidebar.php      |  80 +++
 .../Constraint/AssertPriceInShoppingCart.php  |  75 +++
 .../AssertProductQtyInMiniShoppingCart.php    |  75 +++
 .../AssertProductQtyInShoppingCart.php        |  75 +++
 .../AssertSubtotalInShoppingCart.php          |  76 +++
 .../Checkout/Test/etc/global/constraint.xml   |  39 ++
 .../Checkout/Test/etc/global/fixture.xml      |  32 ++
 .../Handler/CmsPage/CmsPageInterface.php}     |  27 +-
 .../Magento/Cms/Test/Handler/CmsPage/Curl.php |  80 +++
 .../app/Magento/Cms/Test/Page/CmsIndex.php    |  14 +
 .../app/Magento/Cms/Test/Page/CmsIndex.xml    |   6 +
 .../AssertConfigurableInCategory.php          |   2 +-
 .../Constraint/AssertConfigurableView.php     |   2 +-
 .../Constraint/AssertProductRatingInGrid.php  |  20 +-
 .../AssertProductRatingNotInGrid.php          |  70 +++
 ...ssertProductRatingSuccessDeleteMessage.php |  71 +++
 .../Magento/Review/Test/Fixture/Rating.php    |   4 +-
 .../Review/Test/Handler/Rating/Curl.php       |  96 ++++
 .../Test/Handler/Rating/RatingInterface.php   |  35 ++
 .../Magento/Review/Test/Repository/Rating.php |  48 ++
 ....php => CreateProductRatingEntityTest.php} |   4 +-
 .../testCreateProductRatingEntityTest.csv}    |   0
 .../DeleteProductRatingEntityTest.php         | 105 ++++
 .../testDeleteProductRatingEntity.csv         |   2 +
 .../app/Magento/Review/Test/etc/curl/di.xml   |  28 ++
 .../Review/Test/etc/global/constraint.xml     |   6 +
 .../Adminhtml/Rate/Edit/FormPageActions.php   |   7 +
 .../Test/Block/Adminhtml/Rule/Edit/Form.php   |  12 +
 .../Constraint/AssertTaxRateNotInGrid.php     |  74 +++
 .../Constraint/AssertTaxRateNotInTaxRule.php  |  71 +++
 .../AssertTaxRateSuccessDeleteMessage.php     |  71 +++
 .../Test/TestCase/DeleteTaxRateEntityTest.php |  99 ++++
 .../testDeleteTaxRate.csv                     |   2 +
 .../Tax/Test/etc/global/constraint.xml        |  22 +
 .../{ => Adminhtml}/Catalog/Category/Grid.php |  11 +-
 .../{ => Adminhtml}/Catalog/Category/Tree.php |  21 +-
 .../{ => Adminhtml}/Catalog/Edit/Form.php     |   2 +-
 .../{ => Adminhtml}/Catalog/Edit/Form.xml     |   0
 .../{ => Adminhtml}/Catalog/Product/Grid.php  |  42 +-
 .../Test/Block/Adminhtml/Cms/Page/Grid.php    |  52 ++
 .../Test/Block/{ => Adminhtml}/Selector.php   |   2 +-
 .../AssertUrlRewriteCmsPageRedirect.php       |  88 ++++
 ...hp => AssertUrlRewriteProductRedirect.php} |  16 +-
 .../UrlRewrite/Test/Fixture/UrlRewrite.php    |  12 +-
 .../UrlRewrite/Test/Fixture/UrlRewrite.xml    |   5 +-
 .../UrlRewrite/{ProductId.php => IdPath.php}  |  31 +-
 .../Test/Handler/UrlRewrite/Curl.php          |  20 +-
 .../UrlRewrite/UrlRewriteInterface.php        |   2 +-
 .../Test/Page/Adminhtml/UrlrewriteEdit.php    |  46 +-
 .../Test/Page/Adminhtml/UrlrewriteEdit.xml    |  22 +-
 .../Test/Page/Adminhtml/UrlrewriteIndex.php   |   4 +-
 .../Test/Page/Adminhtml/UrlrewriteIndex.xml   |   2 +-
 .../CreateCmsPageRewriteEntityTest.php        | 106 ++++
 .../testCmsPageRewrite.csv                    |   5 +
 .../CreateProductUrlRewriteEntityTest.php     | 104 ++++
 .../testProductUrlRewrite.csv                 |   4 +
 .../DeleteCategoryUrlRewriteEntityTest.php    |  99 ++++
 .../testDeleteCategoryUrlRewrite.csv          |   3 +
 .../DeleteProductUrlRewriteEntityTest.php     |  25 +-
 .../testDeleteProductUrlRewrite.csv           |   4 +-
 .../UpdateCategoryUrlRewriteEntityTest.php    |   2 +-
 .../UpdateProductUrlRewriteEntityTest.php     |   2 +-
 .../testUpdateProductUrlRewrite.csv           |   5 +-
 .../UrlRewrite/Test/etc/global/constraint.xml |  12 +-
 .../UrlRewrite/Test/etc/global/page.xml       |   5 +
 dev/tests/integration/phpunit.xml.dist        |   2 +-
 .../Catalog/Block/Product/AbstractTest.php    |   5 +-
 .../Attribute/Backend/TierpriceTest.php       |   2 +-
 .../Catalog/Model/Product/Type/PriceTest.php  |   2 +-
 .../attribute_set_with_image_attribute.php    |  61 +++
 ...bute_set_with_image_attribute_rollback.php |  54 ++
 .../Catalog/_files/product_group_prices.php   |  68 +++
 .../_files/product_group_prices_rollback.php  |  39 ++
 .../Magento/Catalog/_files/product_image.php  |  23 +-
 .../Magento/Catalog/_files/product_simple.php |   6 +
 .../Catalog/_files/product_with_image.php     |  41 +-
 .../_files/product_with_image_rollback.php    |   1 +
 .../Magento/Store/_files/website.php          |  28 ++
 .../Magento/Store/_files/website_rollback.php |  41 ++
 .../Tax/Model/Sales/Total/Quote/SetupUtil.php |  25 +-
 .../Tax/Model/Sales/Total/Quote/TaxTest.php   |   3 +-
 ...xcluding_tax_apply_tax_before_discount.php |   1 +
 ...luding_tax_cross_border_trade_disabled.php | 107 ++++
 ...cluding_tax_cross_border_trade_enabled.php | 107 ++++
 ...i_tax_rule_total_calculate_subtotal_no.php | 152 ++++++
 ..._tax_rule_total_calculate_subtotal_yes.php | 152 ++++++
 ...ti_tax_rule_unit_calculate_subtotal_no.php | 152 ++++++
 ...i_tax_rule_unit_calculate_subtotal_yes.php | 152 ++++++
 .../tax_calculation_data_aggregated.php       |   7 +-
 .../Widget/Model/Widget/InstanceTest.php      |  10 -
 .../Magento/Widget/Model/WidgetTest.php       |  46 +-
 .../Magento/Widget/_files/themes.php          |  38 --
 .../TestFramework/Utility/ChangedFiles.php    |  57 +++
 .../Magento/Test/Legacy/ObsoleteCodeTest.php  |   2 +-
 .../Pricing/Adjustment/CalculatorTest.php     |  25 +-
 .../Pricing/Price/BundleOptionPriceTest.php   |  17 +-
 .../Bundle/Pricing/Price/GroupPriceTest.php   | 216 +++++---
 .../Model/Product/PriceModifierTest.php       | 161 ++++++
 .../Catalog/Model/ProductRepositoryTest.php   |  76 +++
 .../Catalog/Pricing/Price/GroupPriceTest.php  | 319 ++++++------
 .../Data/GalleryEntryContentValidatorTest.php | 167 +++++++
 .../Media/Data/_files/magento_image.jpg       | Bin 0 -> 13873 bytes
 .../Media/GalleryEntryResolverTest.php        |  77 +++
 .../Attribute/Media/ReadServiceTest.php       | 473 ++++++++++++++++++
 .../Attribute/Media/WriteServiceTest.php      | 324 ++++++++++++
 .../AttributeGroup/ReadServiceTest.php        |  38 +-
 .../AttributeGroup/WriteServiceTest.php       |  78 ++-
 .../V1/Product/GroupPriceServiceTest.php      | 338 +++++++++++++
 .../V1/Product/TierPriceServiceTest.php       | 357 +++++++++++++
 .../Resource/Attribute/CollectionTest.php     | 230 +++++++++
 .../Magento/SalesRule/Model/ValidatorTest.php |  46 +-
 .../Magento/Tax/Model/ConfigTest.php          | 405 +++++++++++++++
 .../Magento/Tax/Pricing/AdjustmentTest.php    |   8 +
 .../Magento/Framework/AppInterface.php        |   2 +-
 185 files changed, 9604 insertions(+), 693 deletions(-)
 create mode 100644 app/code/Magento/Catalog/Model/Product/PriceModifier.php
 create mode 100644 app/code/Magento/Catalog/Model/ProductRepository.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Data/Product/GroupPrice.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Data/Product/GroupPriceBuilder.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Data/Product/TierPrice.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Data/Product/TierPriceBuilder.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntry.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryBuilder.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContent.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentBuilder.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidator.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImage.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImageBuilder.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolver.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadService.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceInterface.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteService.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceInterface.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/GroupPriceService.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/GroupPriceServiceInterface.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/TierPriceService.php
 create mode 100644 app/code/Magento/Catalog/Service/V1/Product/TierPriceServiceInterface.php
 create mode 100644 app/code/Magento/Tax/Model/Config/Notification.php
 create mode 100644 app/code/Magento/Tax/Model/System/Message/Notifications.php
 create mode 100644 app/code/Magento/Tax/etc/adminhtml/di.xml
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInMiniShoppingCart.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/constraint.xml
 create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/fixture.xml
 rename dev/tests/functional/tests/app/Magento/{UrlRewrite/Test/Block/Actions.php => Cms/Test/Handler/CmsPage/CmsPageInterface.php} (70%)
 create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/Curl.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingNotInGrid.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingSuccessDeleteMessage.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/Curl.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/RatingInterface.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/Repository/Rating.php
 rename dev/tests/functional/tests/app/Magento/Review/Test/TestCase/{CreateBackendProductRatingTest.php => CreateProductRatingEntityTest.php} (96%)
 rename dev/tests/functional/tests/app/Magento/Review/Test/TestCase/{CreateBackendProductRatingTest/testCreateBackendProductRating.csv => CreateProductRatingEntityTest/testCreateProductRatingEntityTest.csv} (100%)
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest/testDeleteProductRatingEntity.csv
 create mode 100644 dev/tests/functional/tests/app/Magento/Review/Test/etc/curl/di.xml
 create mode 100644 dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInGrid.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInTaxRule.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateSuccessDeleteMessage.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest.php
 create mode 100644 dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest/testDeleteTaxRate.csv
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Catalog/Category/Grid.php (81%)
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Catalog/Category/Tree.php (72%)
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Catalog/Edit/Form.php (94%)
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Catalog/Edit/Form.xml (100%)
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Catalog/Product/Grid.php (69%)
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Cms/Page/Grid.php
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/{ => Adminhtml}/Selector.php (96%)
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCmsPageRedirect.php
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/{AssertProductUrlAvailableOnTheFront.php => AssertUrlRewriteProductRedirect.php} (82%)
 rename dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/{ProductId.php => IdPath.php} (74%)
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest.php
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest/testCmsPageRewrite.csv
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.php
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest/testProductUrlRewrite.csv
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.php
 create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest/testDeleteCategoryUrlRewrite.csv
 create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices_rollback.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/website.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/website_rollback.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_disabled.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_enabled.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_no.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_no.php
 create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_yes.php
 delete mode 100644 dev/tests/integration/testsuite/Magento/Widget/_files/themes.php
 create mode 100644 dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Model/Product/PriceModifierTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidatorTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/_files/magento_image.jpg
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolverTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/GroupPriceServiceTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/TierPriceServiceTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Eav/Model/Resource/Attribute/CollectionTest.php
 create mode 100644 dev/tests/unit/testsuite/Magento/Tax/Model/ConfigTest.php

diff --git a/.travis.yml b/.travis.yml
index 900ffd8e620..5aa3b310f5f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,7 +28,7 @@ before_script:
   # Create DB for Integration tests
   - sh -c "if [ '$TEST_SUITE' = 'integration' ] || [ '$TEST_SUITE' = 'integration_integrity' ]; then mysql -e 'create database magento_integration_tests;'; mv dev/tests/integration/etc/local-mysql.travis.xml.dist dev/tests/integration/etc/local-mysql.xml; fi"
   # Install tools for static tests
-  - sh -c "if [ '$TEST_SUITE' = 'static_phpcs' ] || [ '$TEST_SUITE' = 'static_annotation' ]; then pear install pear/PHP_CodeSniffer-1.4.7; fi"
+  - sh -c "if [ '$TEST_SUITE' = 'static_phpcs' ] || [ '$TEST_SUITE' = 'static_annotation' ]; then pear install pear/PHP_CodeSniffer-1.5.2; fi"
   - phpenv rehash;
 script:
   # Unit tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23ebdb30c71..74d8042f568 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,42 @@
+2.0.0.0-dev83
+=============
+* Created the Service API for the Magento_Catalog Module:
+   * Product Attribute Media API
+   * Product Group Price API
+* Tax calculation updates:
+  * Fixed tax calculation rounding issues which appeared when a discount was applied
+  * Fixed extra penny issue which appeared when exact tax amount ended with 0.5 cent
+  * Fixed tax calculation issues which appeared when a customer tax rate was different from the store tax rate
+  * Fixed price inconsistencies between catalog and shopping cart
+  * Added support for maintaining consistent prices including tax for customers with different tax rates
+  * Added support for applying tax rules with different priorities to be applied to subtotal only
+  * Added support for tax rounding at individual tax rate
+* Porting Tax Features from Magento 1.x:
+  * Price consistency UX and algorithm
+  * Canadian provincial sales taxes
+  * Fixed issues with bundle product price inconsistency across the system
+  * Added warnings if invalid tax configuration is created in the Admin panel
+  * Fixed issues with regards to hidden tax
+* Fixed bugs:
+  * Fixed an issue where grouped price was not applied for grouped products
+  * Fixed an issue where a fatal error occurred when opening a grouped product page without assigned products on the frontend
+  * Fixed an issue where it was possible to apply an inactive discount coupon
+  * Fixed an issue where the linked products information was lost when exporting products
+  * Fixed non-informative error messages for "Attribute Group Service"
+  * Fixed the invalid default value of the "apply_after_discount" tax setting
+  * Fixed an issue where the integration tests coverage whitelist was broken
+  * Fixed Admin panel UI issues: grids, headers and footers
+* Added the following functional tests:
+  * Create Product Url Rewrite
+  * Delete Catalog Price Rule
+  * Delete Category Url Rewrite
+  * Delete CMS Page Rewrite
+  * Delete Product Rating
+  * Delete Sales Rule
+  * Delete Tax Rate
+  * Update Catalog Price Rule
+  * Update Shopping Cart
+
 2.0.0.0-dev82
 =============
 * Added support for MTF Reporting Tool
diff --git a/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml b/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml
index 7c7dbf54fa7..f54c5cc9f72 100644
--- a/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml
+++ b/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml
@@ -30,7 +30,7 @@
         <block class="Magento\AdminNotification\Block\Window" name="notification_window" as="notification_window" acl="Magento_AdminNotification::show_toolbar" template="notification/window.phtml"/>
     </referenceContainer>
     <referenceContainer name="header">
-        <block class="Magento\AdminNotification\Block\ToolbarEntry" template="toolbar_entry.phtml" />
+        <block class="Magento\AdminNotification\Block\ToolbarEntry" before="user" template="toolbar_entry.phtml" />
     </referenceContainer>
     <referenceBlock name="head">
         <block class="Magento\Theme\Block\Html\Head\Script" name="magento-adminnotification-toolbar-entry-js" after="jquery-jquery-js">
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
index 511001916c6..02ecf79e0bc 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
@@ -24,12 +24,31 @@
 namespace Magento\Bundle\Block\Catalog\Product\View\Type;
 
 use Magento\Framework\Pricing\PriceCurrencyInterface;
+use Magento\Tax\Model\Calculation;
 
 /**
  * Catalog bundle product info block
  */
 class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView
 {
+    /**
+     * constants for different rounding methods
+     */
+    const UNIT_ROUNDING = 0;
+    const ROW_ROUNDING = 1;
+    const TOTAL_ROUNDING = 2;
+
+    /**
+     * Mapping between constants in \Magento\Tax\Model\Calculation and this class
+     *
+     * @var array
+     */
+    protected $mapping = [
+        Calculation::CALC_UNIT_BASE => self::UNIT_ROUNDING,
+        Calculation::CALC_ROW_BASE => self::ROW_ROUNDING,
+        Calculation::CALC_TOTAL_BASE => self::TOTAL_ROUNDING,
+    ];
+
     /**
      * @var array
      */
@@ -305,4 +324,16 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView
         }
         return $optionBlock->setOption($option)->toHtml();
     }
+
+    /**
+     * Return the rounding method based on tax calculation
+     * This is a workaround as the proper way is to always call tax service to get taxed price
+     *
+     * @return int
+     */
+    public function getRoundingMethod()
+    {
+        $algorithm = $this->_taxData->getCalculationAgorithm();
+        return isset($this->mapping[$algorithm]) ? $this->mapping[$algorithm] : self::TOTAL_ROUNDING;
+    }
 }
diff --git a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php
index 0c32a23043f..ac700c144f5 100644
--- a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php
+++ b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php
@@ -31,6 +31,9 @@ use Magento\Bundle\Pricing\Price\BundleSelectionFactory;
 use Magento\Framework\Pricing\Adjustment\Calculator as CalculatorBase;
 use Magento\Bundle\Model\Product\Price;
 use Magento\Bundle\Pricing\Price\BundleOptionPrice;
+use Magento\Tax\Model\Calculation as TaxCalculation;
+use Magento\Store\Model\Store;
+use Magento\Tax\Helper\Data as TaxHelper;
 
 /**
  * Bundle price calculator
@@ -52,20 +55,29 @@ class Calculator implements BundleCalculatorInterface
      */
     protected $selectionFactory;
 
+    /**
+     * Tax helper, needed to get rounding setting
+     *
+     * @var TaxHelper
+     */
+    protected $taxHelper;
     /**
      * @param CalculatorBase $calculator
      * @param AmountFactory $amountFactory
      * @param BundleSelectionFactory $bundleSelectionFactory
+     * @param TaxHelper $taxHelper
      * @return Calculator
      */
     public function __construct(
         CalculatorBase $calculator,
         AmountFactory $amountFactory,
-        BundleSelectionFactory $bundleSelectionFactory
+        BundleSelectionFactory $bundleSelectionFactory,
+        TaxHelper $taxHelper
     ) {
         $this->calculator = $calculator;
         $this->amountFactory = $amountFactory;
         $this->selectionFactory = $bundleSelectionFactory;
+        $this->taxHelper = $taxHelper;
     }
 
     /**
@@ -254,11 +266,24 @@ class Calculator implements BundleCalculatorInterface
         foreach ($selectionPriceList as $selectionPrice) {
             $amountList[] = $selectionPrice->getAmount();
         }
+        /** @var  Store $store */
+        $store = $bundleProduct->getStore();
+        $roundingMethod = $this->taxHelper->getCalculationAgorithm($store);
         /** @var \Magento\Framework\Pricing\Amount\AmountInterface $itemAmount */
         foreach ($amountList as $itemAmount) {
-            $fullAmount += $itemAmount->getValue();
-            foreach ($itemAmount->getAdjustmentAmounts() as $code => $adjustment) {
-                $adjustments[$code] = isset($adjustments[$code]) ? $adjustments[$code] + $adjustment : $adjustment;
+            if ($roundingMethod != TaxCalculation::CALC_TOTAL_BASE) {
+                //We need to round the individual selection first
+                $fullAmount += $store->roundPrice($itemAmount->getValue());
+                foreach ($itemAmount->getAdjustmentAmounts() as $code => $adjustment) {
+                    $adjustment = $store->roundPrice($adjustment);
+                    $adjustments[$code] = isset($adjustments[$code]) ? $adjustments[$code] + $adjustment : $adjustment;
+                }
+            } else {
+                $fullAmount += $itemAmount->getValue();
+                foreach ($itemAmount->getAdjustmentAmounts() as $code => $adjustment) {
+                    $adjustments[$code] = isset($adjustments[$code]) ? $adjustments[$code] + $adjustment : $adjustment;
+                }
+
             }
         }
         if ($exclude && isset($adjustments[$exclude])) {
diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml
index c6fcace245b..29926a7a454 100644
--- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml
+++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml
@@ -32,6 +32,7 @@
             $(document).ready(function() {
                 $('#product_addtocart_form').mage('bundleOption', {
                     "bundleConfig": <?php echo $this->getJsonConfig() ?>,
+                    "roundingMethod" : "<?php echo $this->getRoundingMethod() ?>",
                     "bundleOptionQtyPrefix": "#bundle-option-",
                     "bundleOptionQtySuffix": "-qty-input",
                     "priceSelectors": {
diff --git a/app/code/Magento/Bundle/view/frontend/web/bundle.js b/app/code/Magento/Bundle/view/frontend/web/bundle.js
index 4ac75e5ae60..0310fe5aa06 100644
--- a/app/code/Magento/Bundle/view/frontend/web/bundle.js
+++ b/app/code/Magento/Bundle/view/frontend/web/bundle.js
@@ -251,6 +251,10 @@
         },
 
         selectionPrice: function(optionId, selectionId) {
+            //Those constants need to be in sync with Magento\Bundle\Block\Catalog\Product\View\Type\Bundle
+            var TOTAL_ROUNDING = 2;
+            var ROW_ROUNDING = 1;
+            var UNIT_ROUNDING = 0;
             var qty = null,
                 config = this.options.bundleConfig,
                 configOption = config.options[optionId];
@@ -281,7 +285,19 @@
                 });
             }
 
-            return [price * qty, exclTaxPrice * qty, inclTaxPrice * qty];
+            if (this.options.bundleConfig.isFixedPrice || this.options.roundingMethod == TOTAL_ROUNDING) {
+                return [price * qty, exclTaxPrice * qty, inclTaxPrice * qty];
+            } else if (this.options.roundingMethod == UNIT_ROUNDING) {
+                price = Math.round(price * 100) / 100;
+                exclTaxPrice = Math.round(exclTaxPrice * 100) / 100;
+                inclTaxPrice = Math.round(inclTaxPrice * 100) / 100;
+                return [price * qty, exclTaxPrice * qty, inclTaxPrice * qty];
+            } else {
+                var rowTotal = Math.round(price * qty * 100) /100;
+                var rowTotalExclTax = Math.round(exclTaxPrice * qty * 100) /100;
+                var rowTotalInclTax = Math.round(inclTaxPrice * qty * 100) /100;
+                return [rowTotal, rowTotalExclTax, rowTotalInclTax];
+            }
         },
 
         populateQty: function(optionId, selectionId) {
diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php
index 546eaa128f4..031273808fa 100644
--- a/app/code/Magento/Catalog/Block/Product/View.php
+++ b/app/code/Magento/Catalog/Block/Product/View.php
@@ -236,7 +236,7 @@ class View extends AbstractProduct implements \Magento\Framework\View\Block\Iden
             return $this->_jsonEncoder->encode($config);
         }
 
-        $request = $this->_taxCalculation->getRateRequest(false, false, false);
+        $request = $this->_taxCalculation->getDefaultRateRequest();
         /* @var $product \Magento\Catalog\Model\Product */
         $product = $this->getProduct();
         $request->setProductClassId($product->getTaxClassId());
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Groupprice/AbstractGroupprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Groupprice/AbstractGroupprice.php
index 93e54cbbd9e..8f726dfda1b 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Groupprice/AbstractGroupprice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Groupprice/AbstractGroupprice.php
@@ -169,6 +169,11 @@ abstract class AbstractGroupprice extends Price
             if (isset($duplicates[$compare])) {
                 throw new \Magento\Framework\Model\Exception($this->_getDuplicateErrorMessage());
             }
+
+            if (!preg_match('/^\d*(\.|,)?\d{0,4}$/i', $priceRow['price']) || $priceRow['price'] < 0) {
+                return __('Group price must be a number greater than 0.');
+            }
+
             $duplicates[$compare] = true;
         }
 
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media.php
index a6b2cd70383..3f007f47d41 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media.php
@@ -346,6 +346,7 @@ class Media extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
 
         $this->_getResource()->deleteGallery($recordsToDelete);
         $this->removeDeletedImages($filesToDelete);
+        $object->setData($attrCode, $value);
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/Product/PriceModifier.php b/app/code/Magento/Catalog/Model/Product/PriceModifier.php
new file mode 100644
index 00000000000..01373135fa1
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/PriceModifier.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Catalog\Model\Product;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Exception\CouldNotSaveException;
+
+class PriceModifier
+{
+    /**
+     * @param \Magento\Catalog\Model\Product $product
+     * @param int $customerGroupId
+     * @param int $websiteId
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @return void
+     */
+    public function removeGroupPrice(\Magento\Catalog\Model\Product $product, $customerGroupId, $websiteId)
+    {
+        $prices = $product->getData('group_price');
+        if (is_null($prices)) {
+            throw new NoSuchEntityException("This product doesn't have group price");
+        }
+        $groupPriceQty = count($prices);
+
+        foreach ($prices as $key => $groupPrice) {
+            if ($groupPrice['cust_group'] == $customerGroupId
+                && intval($groupPrice['website_id']) === intval($websiteId)) {
+                unset ($prices[$key]);
+            }
+        }
+        if ($groupPriceQty == count($prices)) {
+            throw new NoSuchEntityException(
+                "Product hasn't group price with such data: customerGroupId = '$customerGroupId',"
+                . "website = $websiteId."
+            );
+        }
+        $product->setData('group_price', $prices);
+        try {
+            $product->save();
+        } catch (\Exception $exception) {
+            throw new CouldNotSaveException("Invalid data provided for group price");
+        }
+    }
+
+    /**
+     * @param \Magento\Catalog\Model\Product $product
+     * @param int|string $customerGroupId
+     * @param int $qty
+     * @param int $websiteId
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @return void
+     */
+    public function removeTierPrice(\Magento\Catalog\Model\Product $product, $customerGroupId, $qty, $websiteId)
+    {
+        $prices = $product->getData('tier_price');
+        // verify if price exist
+        if (is_null($prices)) {
+            throw new NoSuchEntityException("This product doesn't have tier price");
+        }
+        $tierPricesQty = count($prices);
+
+        foreach ($prices as $key => $tierPrice) {
+            if ($customerGroupId == 'all' && $tierPrice['price_qty'] == $qty
+                && $tierPrice['all_groups'] == 1 && intval($tierPrice['website_id']) === intval($websiteId)) {
+                unset ($prices[$key]);
+            } elseif ($tierPrice['price_qty'] == $qty && $tierPrice['cust_group'] == $customerGroupId
+                && intval($tierPrice['website_id']) === intval($websiteId)) {
+                unset ($prices[$key]);
+            }
+        }
+
+        if ($tierPricesQty == count($prices)) {
+            throw new NoSuchEntityException(
+                "Product hasn't group price with such data: customerGroupId = '$customerGroupId',"
+                . "website = $websiteId, qty = $qty"
+            );
+        }
+        $product->setData('tier_price', $prices);
+        try {
+            $product->save();
+        } catch (\Exception $exception) {
+            throw new CouldNotSaveException("Invalid data provided for tier_price");
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
new file mode 100644
index 00000000000..8dc31bb44c1
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Model;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+
+class ProductRepository
+{
+    /**
+     * @var ProductFactory
+     */
+    protected $productFactory;
+
+    /**
+     * @var Product[]
+     */
+    protected $instances = array();
+
+    /**
+     * @param ProductFactory $productFactory
+     */
+    public function __construct(ProductFactory $productFactory)
+    {
+        $this->productFactory = $productFactory;
+    }
+
+    /**
+     * Retrieve product instance by sku
+     *
+     * @param string $sku
+     * @param boolean $editMode
+     * @return Product
+     * @throws NoSuchEntityException
+     */
+    public function get($sku, $editMode = false)
+    {
+        if (!isset($this->instances[$sku])) {
+            $product = $this->productFactory->create();
+            $productId = $product->getIdBySku($sku);
+            if (!$productId) {
+                throw new NoSuchEntityException('Requested product doesn\'t exist');
+            }
+            if ($editMode) {
+                $product->setData('_edit_mode', true);
+            }
+            $product->load($productId);
+            $this->instances[$sku] = $product;
+        }
+        return $this->instances[$sku];
+    }
+}
diff --git a/app/code/Magento/Catalog/Pricing/Price/GroupPrice.php b/app/code/Magento/Catalog/Pricing/Price/GroupPrice.php
index 9fcec9c00de..626f76151d2 100644
--- a/app/code/Magento/Catalog/Pricing/Price/GroupPrice.php
+++ b/app/code/Magento/Catalog/Pricing/Price/GroupPrice.php
@@ -100,21 +100,16 @@ class GroupPrice extends AbstractPrice implements GroupPriceInterface, BasePrice
      */
     public function getStoredGroupPrice()
     {
-        if (null !== $this->storedGroupPrice) {
-            return $this->storedGroupPrice;
-        }
-
-        $this->storedGroupPrice = $this->product->getData('group_price');
-
         if (null === $this->storedGroupPrice) {
-            $attribute = $this->product->getResource()->getAttribute('group_price');
+            $resource = $this->product->getResource();
+            $attribute =  $resource->getAttribute('group_price');
             if ($attribute) {
                 $attribute->getBackend()->afterLoad($this->product);
                 $this->storedGroupPrice = $this->product->getData('group_price');
             }
-        }
-        if (null === $this->storedGroupPrice || !is_array($this->storedGroupPrice)) {
-            $this->storedGroupPrice = [];
+            if (null === $this->storedGroupPrice || !is_array($this->storedGroupPrice)) {
+                $this->storedGroupPrice = [];
+            }
         }
         return $this->storedGroupPrice;
     }
diff --git a/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPrice.php b/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPrice.php
new file mode 100644
index 00000000000..83e8212c9e9
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPrice.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Data\Product;
+
+use Magento\Framework\Service\Data\AbstractObject;
+
+class GroupPrice extends AbstractObject
+{
+    const CUSTOMER_GROUP_ID = 'customer_group_id';
+    const VALUE = 'value';
+
+    /**
+     * Retrieve customer group id
+     *
+     * @return int
+     */
+    public function getCustomerGroupId()
+    {
+        return $this->_get(self::CUSTOMER_GROUP_ID);
+    }
+
+    /**
+     * Retrieve price value
+     *
+     * @return float
+     */
+    public function getValue()
+    {
+        return $this->_get(self::VALUE);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPriceBuilder.php b/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPriceBuilder.php
new file mode 100644
index 00000000000..3210e1dc84f
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Data/Product/GroupPriceBuilder.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Data\Product;
+
+use Magento\Framework\Service\Data\AbstractObjectBuilder;
+
+class GroupPriceBuilder extends AbstractObjectBuilder
+{
+    /**
+     * Set customer group id
+     *
+     * @param int $customerGroupId
+     * @return $this
+     */
+    public function setCustomerGroupId($customerGroupId)
+    {
+        $this->_set(GroupPrice::CUSTOMER_GROUP_ID, $customerGroupId);
+        return $this;
+    }
+
+    /**
+     * Set price value
+     *
+     * @param float $value
+     * @return $this
+     */
+    public function setValue($value)
+    {
+        $this->_set(GroupPrice::VALUE, $value);
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Data/Product/TierPrice.php b/app/code/Magento/Catalog/Service/V1/Data/Product/TierPrice.php
new file mode 100644
index 00000000000..75e554ed306
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Data/Product/TierPrice.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Data\Product;
+
+use Magento\Framework\Service\Data\AbstractObject;
+
+class TierPrice extends AbstractObject
+{
+    const QTY = 'qty';
+
+    const VALUE = 'value';
+
+    /**
+     * Retrieve tier qty
+     *
+     * @return float
+     */
+    public function getQty()
+    {
+        return $this->_get(self::QTY);
+    }
+
+    /**
+     * Retrieve price value
+     *
+     * @return float
+     */
+    public function getValue()
+    {
+        return $this->_get(self::VALUE);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Data/Product/TierPriceBuilder.php b/app/code/Magento/Catalog/Service/V1/Data/Product/TierPriceBuilder.php
new file mode 100644
index 00000000000..4903056618b
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Data/Product/TierPriceBuilder.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Data\Product;
+
+use Magento\Framework\Service\Data\AbstractObjectBuilder;
+
+class TierPriceBuilder extends AbstractObjectBuilder
+{
+    /**
+     * Set Quantity
+     *
+     * @param float $qty
+     * @return $this
+     */
+    public function setQty($qty)
+    {
+        $this->_set(TierPrice::QTY, $qty);
+        return $this;
+    }
+
+    /**
+     * Set Value
+     *
+     * @param float $value
+     * @return $this
+     */
+    public function setValue($value)
+    {
+        $this->_set(TierPrice::VALUE, $value);
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntry.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntry.php
new file mode 100644
index 00000000000..489990c5926
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntry.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Product Media Attribute
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+use \Magento\Framework\Service\Data\AbstractObject;
+
+class GalleryEntry extends AbstractObject
+{
+    const ID = 'id';
+    const LABEL = 'label';
+    const POSITION = 'position';
+    const DISABLED = 'disabled';
+    const TYPES = 'types';
+    const FILE = 'file';
+
+    /**
+     * Retrieve gallery entry ID
+     *
+     * @return int|null
+     */
+    public function getId()
+    {
+        return $this->_get(self::ID);
+    }
+
+    /**
+     * Retrieve gallery entry alternative text
+     *
+     * @return string|null
+     */
+    public function getLabel()
+    {
+        return $this->_get(self::LABEL);
+    }
+
+    /**
+     * Retrieve gallery entry position (sort order)
+     *
+     * @return int
+     */
+    public function getPosition()
+    {
+        return $this->_get(self::POSITION);
+    }
+
+    /**
+     * Check if gallery entry is hidden from product page
+     *
+     * @return bool
+     */
+    public function isDisabled()
+    {
+        return $this->_get(self::DISABLED);
+    }
+
+    /**
+     * Retrieve gallery entry image types (thumbnail, image, small_image etc)
+     *
+     * @return string[]|null
+     */
+    public function getTypes()
+    {
+        return $this->_get(self::TYPES);
+    }
+
+    /**
+     * Get file path
+     *
+     * @return string|null
+     */
+    public function getFile()
+    {
+        return $this->_get(self::FILE);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryBuilder.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryBuilder.php
new file mode 100644
index 00000000000..b613592fe07
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryBuilder.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Builder for media attribute
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+use Magento\Framework\Service\Data\AbstractObjectBuilder;
+
+class GalleryEntryBuilder extends AbstractObjectBuilder
+{
+    /**
+     * Set gallery entity ID
+     *
+     * @param int $entityId
+     * @return $this
+     */
+    public function setId($entityId)
+    {
+        return $this->_set(GalleryEntry::ID, $entityId);
+    }
+
+    /**
+     * Set media alternative text
+     *
+     * @param string $label
+     * @return $this
+     */
+    public function setLabel($label)
+    {
+        return $this->_set(GalleryEntry::LABEL, $label);
+    }
+
+    /**
+     * Set gallery entity position (sort order)
+     *
+     * @param int $position
+     * @return $this
+     */
+    public function setPosition($position)
+    {
+        return $this->_set(GalleryEntry::POSITION, $position);
+    }
+
+    /**
+     * Set disabled flag that shows if gallery entity is hidden from product page
+     *
+     * @param bool $isDisabled
+     * @return $this
+     */
+    public function setDisabled($isDisabled)
+    {
+        return $this->_set(GalleryEntry::DISABLED, $isDisabled);
+    }
+
+    /**
+     * Set gallery entry types (thumbnail, image, small_image etc)
+     *
+     * @param array $roles
+     * @return $this
+     */
+    public function setTypes(array $roles)
+    {
+        return $this->_set(GalleryEntry::TYPES, $roles);
+    }
+
+    /**
+     * Set file path
+     *
+     * @param string $file
+     * @return $this
+     */
+    public function setFile($file)
+    {
+        return $this->_set(GalleryEntry::FILE, $file);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContent.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContent.php
new file mode 100644
index 00000000000..87777694aa1
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContent.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Product Media Content
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+use \Magento\Framework\Service\Data\AbstractObject;
+
+class GalleryEntryContent extends AbstractObject
+{
+    const DATA = 'data';
+    const MIME_TYPE = 'mime_type';
+    const NAME = 'name';
+
+    /**
+     * Retrieve media data (base64 encoded content)
+     *
+     * @return string
+     */
+    public function getData()
+    {
+        return $this->_get(self::DATA);
+    }
+
+    /**
+     * Retrieve MIME type
+     *
+     * @return string
+     */
+    public function getMimeType()
+    {
+        return $this->_get(self::MIME_TYPE);
+    }
+
+    /**
+     * Retrieve image name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->_get(self::NAME);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentBuilder.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentBuilder.php
new file mode 100644
index 00000000000..16ee223a7ac
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentBuilder.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Product Media Content Builder
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+use Magento\Framework\Service\Data\AbstractObjectBuilder;
+
+class GalleryEntryContentBuilder extends AbstractObjectBuilder
+{
+    /**
+     * Set media data (base64 encoded content)
+     *
+     * @param string $data
+     * @return $this
+     */
+    public function setData($data)
+    {
+        return $this->_set(GalleryEntryContent::DATA, $data);
+    }
+
+    /**
+     * Set MIME type
+     *
+     * @param string $mimeType
+     * @return $this
+     */
+    public function setMimeType($mimeType)
+    {
+        return $this->_set(GalleryEntryContent::MIME_TYPE, $mimeType);
+    }
+
+    /**
+     * Set image name (without extension)
+     *
+     * @param string $name
+     * @return $this
+     */
+    public function setName($name)
+    {
+        return $this->_set(GalleryEntryContent::NAME, $name);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidator.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidator.php
new file mode 100644
index 00000000000..170b53b0aee
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidator.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Product Media Content Validator
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+use \Magento\Framework\Exception\InputException;
+
+class GalleryEntryContentValidator
+{
+    /**
+     * @var array
+     */
+    private $defaultMimeTypes = array(
+        'image/jpg',
+        'image/jpeg',
+        'image/gif',
+        'image/png',
+    );
+
+    /**
+     * @var array
+     */
+    private $allowedMimeTypes;
+
+    /**
+     * @param array $allowedMimeTypes
+     */
+    public function __construct(
+        array $allowedMimeTypes = array()
+    ) {
+        $this->allowedMimeTypes = array_merge($this->defaultMimeTypes, $allowedMimeTypes);
+    }
+
+    /**
+     * Check if gallery entry content is valid
+     *
+     * @param GalleryEntryContent $entryContent
+     * @return bool
+     * @throws InputException
+     */
+    public function isValid(GalleryEntryContent $entryContent)
+    {
+        $fileContent = @base64_decode($entryContent->getData(), true);
+        if (empty($fileContent)) {
+            throw new InputException('The image content must be valid base64 encoded data.');
+        }
+        $imageProperties = @getimagesizefromstring($fileContent);
+        if (empty($imageProperties)) {
+            throw new InputException('The image content must be valid base64 encoded data.');
+        }
+        $sourceMimeType = $imageProperties['mime'];
+        if ($sourceMimeType != $entryContent->getMimeType() || !$this->isMimeTypeValid($sourceMimeType)) {
+            throw new InputException('The image MIME type is not valid or not supported.');
+        }
+        if (!$this->isNameValid($entryContent->getName())) {
+            throw new InputException('Provided image name contains forbidden characters.');
+        }
+        return true;
+    }
+
+    /**
+     * Check if given mime type is valid
+     *
+     * @param string $mimeType
+     * @return bool
+     */
+    protected function isMimeTypeValid($mimeType)
+    {
+        return in_array($mimeType, $this->allowedMimeTypes);
+    }
+
+    /**
+     * Check if given filename is valid
+     *
+     * @param string $name
+     * @return bool
+     */
+    protected function isNameValid($name)
+    {
+        // Cannot contain \ / : * ? " < > |
+        if (!preg_match('/^[^\\/?*:";<>()|{}\\\\]+$/', $name)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImage.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImage.php
new file mode 100644
index 00000000000..6b4769a693e
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImage.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+/**
+ * Contains media_image attribute info
+ */
+class MediaImage extends \Magento\Framework\Service\Data\AbstractObject
+{
+    const CODE = 'code';
+
+    const SCOPE = 'scope';
+
+    const LABEL = 'frontend_label';
+
+    const IS_USER_DEFINED = 'is_user_defined';
+
+    /**
+     * attribute code
+     *
+     * @return string
+     */
+    public function getCode()
+    {
+        return $this->_get(self::CODE);
+    }
+
+    /**
+     * Return values are 'Global', 'Website' or 'Store View'
+     *
+     * @return string
+     */
+    public function getScope()
+    {
+        return $this->_get(self::SCOPE);
+    }
+
+    /**
+     * Frontend label for attribute
+     *
+     * @return string
+     */
+    public function getFrontendLabel()
+    {
+        return $this->_get(self::LABEL);
+    }
+
+    /**
+     * User defined or system attribute
+     *
+     * @return int 0 or 1
+     */
+    public function getIsUserDefined()
+    {
+        return (int)(bool)$this->_get(self::IS_USER_DEFINED);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImageBuilder.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImageBuilder.php
new file mode 100644
index 00000000000..3689228c473
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/MediaImageBuilder.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+/**
+ * Builder for media_image
+ */
+class MediaImageBuilder extends \Magento\Framework\Service\Data\AbstractObjectBuilder
+{
+    /**
+     * Set attribute code
+     *
+     * @param string $code
+     * @return $this
+     */
+    public function setCode($code)
+    {
+        return $this->_set(MediaImage::CODE, $code);
+    }
+
+    /**
+     * Set attribute frontend label
+     *
+     * @param string $label
+     * @return $this
+     */
+    public function setFrontendLabel($label)
+    {
+        return $this->_set(MediaImage::LABEL, $label);
+    }
+
+    /**
+     * Set attribute scope. Valid values are 'Global', 'Website' and 'Store View'
+     *
+     * @param string $scope
+     * @return $this
+     */
+    public function setScope($scope)
+    {
+        return $this->_set(MediaImage::SCOPE, $scope);
+    }
+
+    /**
+     * Set true for user attributes or false for system attributes
+     *
+     * @param bool $isUserDefined
+     * @return $this
+     */
+    public function setIsUserDefined($isUserDefined)
+    {
+        return $this->_set(MediaImage::IS_USER_DEFINED, $isUserDefined);
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolver.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolver.php
new file mode 100644
index 00000000000..95cea9d34bc
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolver.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Product Media Gallery Entry Resolver
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use \Magento\Catalog\Model\Product;
+
+class GalleryEntryResolver
+{
+    /**
+     * Retrieve file path that corresponds to the given gallery entry ID
+     *
+     * @param Product $product
+     * @param int $entryId
+     * @return string|null
+     */
+    public function getEntryFilePathById(Product $product, $entryId)
+    {
+        $mediaGalleryData = $product->getData('media_gallery');
+        if (!isset($mediaGalleryData['images']) || !is_array($mediaGalleryData['images'])) {
+            return null;
+        }
+
+        foreach ($mediaGalleryData['images'] as $image) {
+            if (isset($image['value_id']) && $image['value_id'] == $entryId) {
+                return isset($image['file']) ? $image['file'] : null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve gallery entry ID that corresponds to the given file path
+     *
+     * @param Product $product
+     * @param string $filePath
+     * @return int|null
+     */
+    public function getEntryIdByFilePath(Product $product, $filePath)
+    {
+        $mediaGalleryData = $product->getData('media_gallery');
+        if (!isset($mediaGalleryData['images']) || !is_array($mediaGalleryData['images'])) {
+            return null;
+        }
+
+        foreach ($mediaGalleryData['images'] as $image) {
+            if (isset($image['file']) && $image['file'] == $filePath) {
+                return isset($image['value_id']) ? $image['value_id'] : null;
+            }
+        }
+        return null;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadService.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadService.php
new file mode 100644
index 00000000000..22794baae06
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadService.php
@@ -0,0 +1,242 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use Magento\Catalog\Service\V1\Product\Attribute\Media\Data\MediaImageBuilder;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Exception\InputException;
+use Magento\Framework\Exception\StateException;
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryBuilder;
+use \Magento\Catalog\Model\Product;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class ReadService implements ReadServiceInterface
+{
+    /**
+     * @var \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory
+     */
+    protected $collectionFactory;
+
+    /**
+     * @var \Magento\Eav\Model\Entity\Attribute\SetFactory
+     */
+    protected $setFactory;
+
+    /** @var \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\MediaImageBuilder */
+    protected $builder;
+
+    /**
+     * @var \Magento\Eav\Model\Config
+     */
+    protected $eavConfig;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductRepository
+     */
+    protected $productRepository;
+
+    /**
+     * @var GalleryEntryBuilder
+     */
+    protected $galleryEntryBuilder;
+    
+    /**
+     * @var \Magento\Catalog\Model\Resource\Product\Attribute\Backend\Media
+     */
+    protected $mediaGallery;
+
+    /**
+     * @var \Magento\Catalog\Model\Resource\Eav\AttributeFactory
+     */
+    protected $attributeFactory;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface
+     */
+    protected $storeManager;
+
+    /**
+     * @param \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $collectionFactory
+     * @param \Magento\Eav\Model\Entity\Attribute\SetFactory $setFactory
+     * @param \Magento\Eav\Model\Config $eavConfig
+     * @param \Magento\Catalog\Model\Resource\Product\Attribute\Backend\Media $mediaGallery
+     * @param \Magento\Catalog\Model\Resource\Eav\AttributeFactory $attributeFactory
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param MediaImageBuilder $mediaImageBuilder
+     * @param \Magento\Catalog\Model\ProductRepository $productRepository
+     * @param GalleryEntryBuilder $galleryEntryBuilder
+     */
+    public function __construct(
+        \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $collectionFactory,
+        \Magento\Eav\Model\Entity\Attribute\SetFactory $setFactory,
+        \Magento\Eav\Model\Config $eavConfig,
+        \Magento\Catalog\Model\Resource\Product\Attribute\Backend\Media $mediaGallery,
+        \Magento\Catalog\Model\Resource\Eav\AttributeFactory $attributeFactory,
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
+        MediaImageBuilder $mediaImageBuilder,
+        \Magento\Catalog\Model\ProductRepository $productRepository,
+        GalleryEntryBuilder $galleryEntryBuilder
+    ) {
+        $this->collectionFactory = $collectionFactory;
+        $this->setFactory = $setFactory;
+        $this->eavConfig = $eavConfig;
+        $this->mediaGallery = $mediaGallery;
+        $this->attributeFactory = $attributeFactory;
+        $this->storeManager = $storeManager;
+        $this->builder = $mediaImageBuilder;
+        $this->productRepository = $productRepository;
+        $this->galleryEntryBuilder = $galleryEntryBuilder;
+    }
+
+    /**
+     * Convert data from array to data object
+     *
+     * @param \Magento\Catalog\Model\Resource\Eav\Attribute[] $items
+     * @return array|\Magento\Catalog\Model\Resource\Eav\Attribute[]
+     * @throws \Magento\Framework\Exception\StateException
+     */
+    protected function prepareData($items)
+    {
+        $data = [];
+        /** @var \Magento\Catalog\Model\Resource\Eav\Attribute $attribute */
+        foreach ($items as $attribute) {
+            $this->builder->setFrontendLabel($attribute->getStoreLabel());
+            $this->builder->setCode($attribute->getData('attribute_code'));
+            $this->builder->setIsUserDefined($attribute->getData('is_user_defined'));
+            if ($attribute->getIsGlobal()) {
+                $scope = 'Global';
+            } elseif ($attribute->isScopeWebsite()) {
+                $scope = 'Website';
+            } elseif ($attribute->isScopeStore()) {
+                $scope = 'Store View';
+            } else {
+                throw new StateException('Attribute has invalid scope. Id = ' . $attribute->getId());
+            }
+            $this->builder->setScope($scope);
+            $data[] = $this->builder->create();
+        }
+        return $data;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function types($attributeSetId)
+    {
+        $attributeSet = $this->setFactory->create()->load($attributeSetId);
+        if (!$attributeSet->getId()) {
+            throw NoSuchEntityException::singleField('attribute_set_id', $attributeSetId);
+        }
+
+        $productEntityId = $this->eavConfig->getEntityType(\Magento\Catalog\Model\Product::ENTITY)->getId();
+        if ($attributeSet->getEntityTypeId() != $productEntityId) {
+            throw InputException::invalidFieldValue('entity_type_id', $attributeSetId);
+        }
+
+        $collection = $this->collectionFactory->create();
+        $collection->setAttributeSetFilter($attributeSetId);
+        $collection->setFrontendInputTypeFilter('media_image');
+        $collection->addStoreLabel($this->storeManager->getStore()->getId());
+
+        return $this->prepareData($collection->getItems());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getList($productSku)
+    {
+        $result = array();
+        /** @var \Magento\Catalog\Model\Product $product */
+        $product = $this->productRepository->get($productSku);
+
+        $galleryAttribute = $this->attributeFactory->create()->loadByCode(
+            $this->eavConfig->getEntityType(\Magento\Catalog\Model\Product::ENTITY),
+            'media_gallery'
+        );
+
+        $container = new \Magento\Framework\Object(array('attribute' => $galleryAttribute));
+        $gallery = $this->mediaGallery->loadGallery($product, $container);
+
+        $productImages = $this->getMediaAttributeValues($product);
+
+        foreach ($gallery as $image) {
+            $this->galleryEntryBuilder->setId($image['value_id']);
+            $this->galleryEntryBuilder->setLabel($image['label_default']);
+            $this->galleryEntryBuilder->setTypes(array_keys($productImages, $image['file']));
+            $this->galleryEntryBuilder->setDisabled($image['disabled_default']);
+            $this->galleryEntryBuilder->setPosition($image['position_default']);
+            $this->galleryEntryBuilder->setFile($image['file']);
+            $result[] = $this->galleryEntryBuilder->create();
+        }
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function info($productSku, $imageId)
+    {
+        try {
+            $product = $this->productRepository->get($productSku);
+        } catch (\Exception $exception) {
+            throw new NoSuchEntityException("Such product doesn't exist");
+        }
+
+        $output = null;
+        $productImages = $this->getMediaAttributeValues($product);
+        foreach ((array)$product->getMediaGallery('images') as $image) {
+            if (intval($image['value_id']) == intval($imageId)) {
+                $image['types'] = array_keys($productImages, $image['file']);
+                $output = $this->galleryEntryBuilder->populateWithArray($image)->create();
+                break;
+            }
+        }
+
+        if (is_null($output)) {
+            throw new NoSuchEntityException("Such image doesn't exist");
+        }
+        return $output;
+    }
+
+    /**
+     * Retrieve assoc array that contains media attribute values of the given product
+     *
+     * @param Product $product
+     * @return array
+     */
+    protected function getMediaAttributeValues(Product $product)
+    {
+        $mediaAttributeCodes = array_keys($product->getMediaAttributes());
+        $mediaAttributeValues = array();
+        foreach ($mediaAttributeCodes as $attributeCode) {
+            $mediaAttributeValues[$attributeCode] = $product->getData($attributeCode);
+        }
+        return $mediaAttributeValues;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceInterface.php
new file mode 100644
index 00000000000..020e95b2e2a
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceInterface.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+interface ReadServiceInterface
+{
+    /**
+     * Return all media attributes for pointed attribute set
+     *
+     * @param int $attributeSetId
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\InputException
+     * @return \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\MediaImage[]
+     */
+    public function types($attributeSetId);
+
+    /**
+     * @param string $productSku
+     * @return \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry[]
+     */
+    public function getList($productSku);
+
+    /**
+     * Return information about gallery entity
+     *
+     * @param string $productSku
+     * @param int $imageId
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @return \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry
+     */
+    public function info($productSku, $imageId);
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteService.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteService.php
new file mode 100644
index 00000000000..0c80699b6dd
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteService.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Product Media Attribute
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry;
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContent;
+use \Magento\Framework\App\Filesystem;
+use \Magento\Catalog\Service\V1\Product\ProductLoader;
+use \Magento\Catalog\Model\Product\Media\Config as MediaConfig;
+use \Magento\Catalog\Model\Product;
+use \Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContentValidator;
+use \Magento\Store\Model\StoreFactory;
+use \Magento\Framework\Exception\InputException;
+use \Magento\Framework\Exception\StateException;
+use \Magento\Framework\Exception\NoSuchEntityException;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class WriteService implements WriteServiceInterface
+{
+    /**
+     * MIME type/extension map
+     *
+     * @var array
+     */
+    private $mimeTypeExtensionMap = array(
+        'image/jpg' => 'jpg',
+        'image/jpeg' => 'jpg',
+        'image/gif' => 'gif',
+        'image/png' => 'png',
+    );
+
+    /**
+     * @var GalleryEntryContentValidator
+     */
+    private $contentValidator;
+
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    /**
+     * @var MediaConfig
+     */
+    private $mediaConfig;
+
+    /**
+     * @var ProductLoader
+     */
+    private $productLoader;
+
+    /**
+     * @var StoreFactory
+     */
+    private $storeFactory;
+
+    /**
+     * @var GalleryEntryResolver
+     */
+    private $entryResolver;
+
+    /**
+     * @param GalleryEntryContentValidator $contentValidator
+     * @param Filesystem $filesystem
+     * @param ProductLoader $productLoader
+     * @param MediaConfig $mediaConfig
+     * @param StoreFactory $storeFactory
+     * @param GalleryEntryResolver $entryResolver
+     */
+    public function __construct(
+        GalleryEntryContentValidator $contentValidator,
+        Filesystem $filesystem,
+        ProductLoader $productLoader,
+        MediaConfig $mediaConfig,
+        StoreFactory $storeFactory,
+        GalleryEntryResolver $entryResolver
+    ) {
+        $this->contentValidator = $contentValidator;
+        $this->filesystem = $filesystem;
+        $this->productLoader = $productLoader;
+        $this->mediaConfig = $mediaConfig;
+        $this->storeFactory = $storeFactory;
+        $this->entryResolver = $entryResolver;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function create($productSku, GalleryEntry $entry, GalleryEntryContent $entryContent, $storeId = 0)
+    {
+        $store = $this->storeFactory->create()->load($storeId);
+        if ($store->getId() != $storeId) {
+            throw new NoSuchEntityException('There is no store with provided ID.');
+        }
+        if (!$this->contentValidator->isValid($entryContent)) {
+            throw new InputException('The image content is not valid.');
+        }
+        $product = $this->productLoader->load($productSku);
+
+        $fileContent = @base64_decode($entryContent->getData(), true);
+        $mediaTmpPath = $this->mediaConfig->getBaseTmpMediaPath();
+        $mediaDirectory = $this->filesystem->getDirectoryWrite(Filesystem::MEDIA_DIR);
+        $mediaDirectory->create($mediaTmpPath);
+        $fileName = $entryContent->getName() . '.' . $this->mimeTypeExtensionMap[$entryContent->getMimeType()];
+        $relativeFilePath = $mediaTmpPath . DIRECTORY_SEPARATOR . $fileName;
+        $absoluteFilePath = $mediaDirectory->getAbsolutePath($relativeFilePath);
+        $mediaDirectory->writeFile($relativeFilePath, $fileContent);
+
+        /** @var $productMediaGallery \Magento\Catalog\Model\Product\Attribute\Backend\Media */
+        $productMediaGallery = $this->getGalleryAttributeBackend($product);
+        $imageFileUri = $productMediaGallery->addImage(
+            $product,
+            $absoluteFilePath,
+            $entry->getTypes(),
+            true,
+            $entry->isDisabled()
+        );
+        // Update additional fields that are still empty after addImage call
+        $productMediaGallery->updateImage($product, $imageFileUri, array(
+            'label' => $entry->getLabel(),
+            'position' => $entry->getPosition(),
+            'disabled' => $entry->isDisabled(),
+        ));
+        $product->setStoreId($storeId);
+        $product->save();
+        // Remove all temporary files
+        $mediaDirectory->delete($relativeFilePath);
+        // File could change its name during the move from tmp dir
+        return $this->entryResolver->getEntryIdByFilePath(
+            $product,
+            $productMediaGallery->getRenamedImage($imageFileUri)
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update($productSku, GalleryEntry $entry, $storeId = 0)
+    {
+        $store = $this->storeFactory->create()->load($storeId);
+        if ($store->getId() != $storeId) {
+            throw new NoSuchEntityException('There is no store with provided ID.');
+        }
+        $product = $this->productLoader->load($productSku);
+        /** @var $productMediaGallery \Magento\Catalog\Model\Product\Attribute\Backend\Media */
+        $productMediaGallery = $this->getGalleryAttributeBackend($product);
+        $filePath = $this->entryResolver->getEntryFilePathById($product, $entry->getId());
+        if (is_null($filePath)) {
+            throw new NoSuchEntityException('There is no image with provided ID.');
+        }
+
+        $productMediaGallery->updateImage($product, $filePath, array(
+            'label' => $entry->getLabel(),
+            'position' => $entry->getPosition(),
+            'disabled' => $entry->isDisabled(),
+        ));
+        $productMediaGallery->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
+        $productMediaGallery->setMediaAttribute($product, $entry->getTypes(), $filePath);
+        $product->setStoreId($storeId);
+        $product->save();
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete($productSku, $entryId)
+    {
+        $product = $this->productLoader->load($productSku);
+        /** @var $productMediaGallery \Magento\Catalog\Model\Product\Attribute\Backend\Media */
+        $productMediaGallery = $this->getGalleryAttributeBackend($product);
+        $filePath = $this->entryResolver->getEntryFilePathById($product, $entryId);
+        if (is_null($filePath)) {
+            throw new NoSuchEntityException('There is no image with provided ID.');
+        }
+
+        $productMediaGallery->removeImage($product, $filePath);
+        $product->save();
+        return true;
+    }
+
+    /**
+     * Retrieve backend model of product media gallery attribute
+     *
+     * @param Product $product
+     * @return \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
+     * @throws StateException
+     */
+    protected function getGalleryAttributeBackend(Product $product)
+    {
+        $attributes = $product->getTypeInstance()->getSetAttributes($product);
+        if (!isset($attributes['media_gallery']) || !($attributes['media_gallery'] instanceof AbstractAttribute)) {
+            throw new StateException('Requested product does not support images.');
+        }
+        /** @var $galleryAttribute AbstractAttribute */
+        $galleryAttribute = $attributes['media_gallery'];
+        return $galleryAttribute->getBackend();
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceInterface.php
new file mode 100644
index 00000000000..c5672db0f63
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceInterface.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Product Media Attribute Write Service
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry;
+use \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContent;
+
+interface WriteServiceInterface
+{
+    /**
+     * Create new gallery entry
+     *
+     * @param string $productSku
+     * @param \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry $entry
+     * @param \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContent $entryContent
+     * @param int $storeId
+     * @return int gallery entry ID
+     * @throws \Magento\Framework\Exception\InputException
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\StateException
+     */
+    public function create($productSku, GalleryEntry $entry, GalleryEntryContent $entryContent, $storeId = 0);
+
+    /**
+     * Update gallery entry
+     *
+     * @param string $productSku
+     * @param \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry $entry
+     * @param int $storeId
+     * @return bool
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\StateException
+     */
+    public function update($productSku, GalleryEntry $entry, $storeId = 0);
+
+    /**
+     * Remove gallery entry
+     *
+     * @param string $productSku
+     * @param int $entryId
+     * @return bool
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\StateException
+     */
+    public function delete($productSku, $entryId);
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadService.php b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadService.php
index 3307a26979b..03ebc1a45e8 100644
--- a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadService.php
+++ b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadService.php
@@ -27,6 +27,8 @@ namespace Magento\Catalog\Service\V1\Product\AttributeGroup;
 
 use \Magento\Catalog\Service\V1\Data;
 use \Magento\Eav\Model\Resource\Entity\Attribute\Group\CollectionFactory as AttributeGroupCollectionFactory;
+use \Magento\Eav\Model\Entity\Attribute\SetFactory as AttributeSetFactory;
+use \Magento\Framework\Exception\NoSuchEntityException;
 
 class ReadService implements ReadServiceInterface
 {
@@ -35,6 +37,11 @@ class ReadService implements ReadServiceInterface
      */
     protected $groupListFactory;
 
+    /**
+     * @var \Magento\Eav\Model\Entity\Attribute\SetFactory
+     */
+    protected $attributeSetFactory;
+
     /**
      * @var \Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder
      */
@@ -42,13 +49,16 @@ class ReadService implements ReadServiceInterface
 
     /**
      * @param AttributeGroupCollectionFactory $groupListFactory
-     * @param \Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder $groupBuilder
+     * @param AttributeSetFactory $attributeSetFactory
+     * @param Data\Eav\AttributeGroupBuilder $groupBuilder
      */
     public function __construct(
         AttributeGroupCollectionFactory $groupListFactory,
+        AttributeSetFactory $attributeSetFactory,
         Data\Eav\AttributeGroupBuilder $groupBuilder
     ) {
         $this->groupListFactory = $groupListFactory;
+        $this->attributeSetFactory = $attributeSetFactory;
         $this->groupBuilder = $groupBuilder;
     }
 
@@ -57,6 +67,10 @@ class ReadService implements ReadServiceInterface
      */
     public function getList($attributeSetId)
     {
+        if (!$this->attributeSetFactory->create()->load($attributeSetId)->getId()) {
+            throw NoSuchEntityException::singleField('attributeSetId', $attributeSetId);
+        }
+
         $collection = $this->groupListFactory->create();
         $collection->setAttributeSetFilter($attributeSetId);
         $collection->setSortOrder();
diff --git a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceInterface.php
index ae4eaba782b..74dc98351c9 100644
--- a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceInterface.php
+++ b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceInterface.php
@@ -31,6 +31,7 @@ interface ReadServiceInterface
      *
      * @param string $attributeSetId
      * @return \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      */
     public function getList($attributeSetId);
 }
diff --git a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteService.php b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteService.php
index b6074d36a26..df387169387 100644
--- a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteService.php
+++ b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteService.php
@@ -26,10 +26,11 @@ namespace Magento\Catalog\Service\V1\Product\AttributeGroup;
 
 use \Magento\Catalog\Model\Product\Attribute\GroupFactory;
 use \Magento\Catalog\Model\Product\Attribute\Group;
-use Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder;
-use Magento\Framework\Exception\CouldNotSaveException;
-use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\Exception\StateException;
+use \Magento\Eav\Model\Entity\Attribute\SetFactory;
+use \Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder;
+use \Magento\Framework\Exception\CouldNotSaveException;
+use \Magento\Framework\Exception\NoSuchEntityException;
+use \Magento\Framework\Exception\StateException;
 
 class WriteService implements WriteServiceInterface
 {
@@ -43,13 +44,23 @@ class WriteService implements WriteServiceInterface
      */
     protected $groupBuilder;
 
+    /**
+     * @var \Magento\Eav\Model\Entity\Attribute\SetFactory
+     */
+    protected $setFactory;
+
     /**
      * @param GroupFactory $groupFactory
+     * @param SetFactory $attributeSetFactory
      * @param AttributeGroupBuilder $groupBuilder
      */
-    public function __construct(GroupFactory $groupFactory, AttributeGroupBuilder $groupBuilder)
-    {
+    public function __construct(
+        GroupFactory $groupFactory,
+        SetFactory $attributeSetFactory,
+        AttributeGroupBuilder $groupBuilder
+    ) {
         $this->groupFactory = $groupFactory;
+        $this->setFactory = $attributeSetFactory;
         $this->groupBuilder = $groupBuilder;
     }
 
@@ -58,6 +69,10 @@ class WriteService implements WriteServiceInterface
      */
     public function create($attributeSetId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData)
     {
+        if (!$this->setFactory->create()->load($attributeSetId)->getId()) {
+            throw NoSuchEntityException::singleField('attributeSetId', $attributeSetId);
+        }
+
         try {
             /** @var Group $attributeGroup */
             $attributeGroup = $this->groupFactory->create();
@@ -70,23 +85,28 @@ class WriteService implements WriteServiceInterface
                 $attributeGroup->getAttributeGroupName()
             )->create();
         } catch (\Exception $e) {
-            throw new CouldNotSaveException('Could not create attribute group');
+            throw new CouldNotSaveException(
+                'Could not create attribute group. Maybe group with such name already exists'
+            );
         }
     }
 
     /**
      * {@inheritdoc}
      */
-    public function update($groupId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData)
+    public function update($attributeSetId, $groupId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData)
     {
         /** @var Group $attributeGroup */
         $attributeGroup = $this->groupFactory->create();
         $attributeGroup->load($groupId);
         if (!$attributeGroup->getId()) {
-            throw new NoSuchEntityException();
+            throw NoSuchEntityException::singleField('attributeGroupId', $attributeGroup->getId());
+        }
+        if ($attributeGroup->getAttributeSetId() != $attributeSetId) {
+            throw new StateException('Attribute group does not belong to provided attribute set');
         }
         try {
-            $attributeGroup->setId($groupData->getId());
+            $attributeGroup->setId($groupId);
             $attributeGroup->setAttributeGroupName($groupData->getName());
             $attributeGroup->save();
         } catch (\Exception $e) {
@@ -97,17 +117,22 @@ class WriteService implements WriteServiceInterface
     /**
      * {@inheritdoc}
      */
-    public function delete($groupId)
+    public function delete($attributeSetId, $groupId)
     {
         /** @var Group $attributeGroup */
         $attributeGroup = $this->groupFactory->create();
         $attributeGroup->load($groupId);
+
+        if (!$attributeGroup->getId()) {
+            throw NoSuchEntityException::singleField('attributeGroupId', $groupId);
+        }
         if ($attributeGroup->hasSystemAttributes()) {
             throw new StateException('Attribute group that contains system attributes can not be deleted');
         }
-        if (!$attributeGroup->getId()) {
-            throw new NoSuchEntityException();
+        if ($attributeGroup->getAttributeSetId() != $attributeSetId) {
+            throw new StateException('Attribute group does not belong to provided attribute set');
         }
         $attributeGroup->delete();
+        return true;
     }
 }
diff --git a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceInterface.php
index 3d3c396539e..9a2515a06e3 100644
--- a/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceInterface.php
+++ b/app/code/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceInterface.php
@@ -33,27 +33,31 @@ interface WriteServiceInterface
      * @param \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData
      * @return \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup
      * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      */
     public function create($attributeSetId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData);
 
     /**
      * Update attribute group
      *
+     * @param string $attributeSetId
      * @param string $groupId
      * @param \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData
      * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\StateException
      * @throws \Magento\Framework\Exception\CouldNotSaveException
      * @return bool
      */
-    public function update($groupId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData);
+    public function update($attributeSetId, $groupId, \Magento\Catalog\Service\V1\Data\Eav\AttributeGroup $groupData);
 
     /**
      * Remove attribute group
      *
+     * @param string $attributeSetId
      * @param string $groupId
      * @throws \Magento\Framework\Exception\NoSuchEntityException
      * @throws \Magento\Framework\Exception\StateException
      * @return bool
      */
-    public function delete($groupId);
+    public function delete($attributeSetId, $groupId);
 }
diff --git a/app/code/Magento/Catalog/Service/V1/Product/GroupPriceService.php b/app/code/Magento/Catalog/Service/V1/Product/GroupPriceService.php
new file mode 100644
index 00000000000..41729855bc5
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/GroupPriceService.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+use Magento\Catalog\Model\ProductFactory;
+use Magento\Catalog\Model\ProductRepository;
+use Magento\Framework\Exception\CouldNotSaveException;
+use Magento\Framework\Exception\InputException;
+use Magento\Catalog\Service\V1\Data\Product;
+
+class GroupPriceService implements GroupPriceServiceInterface
+{
+    /**
+     * @var \Magento\Catalog\Model\ProductRepository
+     */
+    protected $productRepository;
+
+    /**
+     * @var \Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder
+     */
+    protected $groupPriceBuilder;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface
+     */
+    protected $storeManager;
+
+    /**
+     * @var \Magento\Customer\Service\V1\CustomerGroupServiceInterface
+     */
+    protected $customerGroupService;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\PriceModifier
+     */
+    protected $priceModifier;
+
+    /**
+     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     */
+    protected $config;
+
+    /**
+     * @param ProductRepository $productRepository
+     * @param Product\GroupPriceBuilder $groupPriceBuilder
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param \Magento\Customer\Service\V1\CustomerGroupServiceInterface $customerGroupService
+     * @param \Magento\Catalog\Model\Product\PriceModifier $priceModifier
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
+     */
+    public function __construct(
+        ProductRepository $productRepository,
+        Product\GroupPriceBuilder $groupPriceBuilder,
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
+        \Magento\Customer\Service\V1\CustomerGroupServiceInterface $customerGroupService,
+        \Magento\Catalog\Model\Product\PriceModifier $priceModifier,
+        \Magento\Framework\App\Config\ScopeConfigInterface $config
+    ) {
+        $this->productRepository = $productRepository;
+        $this->groupPriceBuilder = $groupPriceBuilder;
+        $this->storeManager = $storeManager;
+        $this->customerGroupService = $customerGroupService;
+        $this->priceModifier = $priceModifier;
+        $this->config = $config;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    public function set($productSku, \Magento\Catalog\Service\V1\Data\Product\GroupPrice $price)
+    {
+        $customerGroup = $this->customerGroupService->getGroup($price->getCustomerGroupId());
+        $product = $this->productRepository->get($productSku, true);
+
+        $groupPrices = $product->getData('group_price');
+        $websiteId = 0;
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) != 0) {
+            $websiteId = $this->storeManager->getWebsite()->getId();
+        }
+        $found = false;
+        foreach ($groupPrices as &$currentPrice) {
+            if (intval($currentPrice['cust_group']) === $price->getCustomerGroupId()
+                && intval($currentPrice['website_id']) === intval($websiteId)
+            ) {
+                $currentPrice['price'] = $price->getValue();
+                $found = true;
+                break;
+            }
+        }
+        if (!$found) {
+            $groupPrices[] = array(
+                'cust_group' => $customerGroup->getId(),
+                'website_id' => $websiteId,
+                'price' => $price->getValue(),
+            );
+        }
+
+        $product->setData('group_price', $groupPrices);
+        $errors = $product->validate();
+        if (is_array($errors) && count($errors)) {
+            $errorAttributeCodes = implode(', ', array_keys($errors));
+            throw new InputException(
+                sprintf('Values of following attributes are invalid: %s', $errorAttributeCodes)
+            );
+        }
+        try {
+            $product->save();
+        } catch (\Exception $e) {
+            throw new CouldNotSaveException('Could not save group price');
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete($productSku, $customerGroupId)
+    {
+        $product = $this->productRepository->get($productSku, true);
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) == 0) {
+            $websiteId = 0;
+        } else {
+            $websiteId = $this->storeManager->getWebsite()->getId();
+        }
+        $this->priceModifier->removeGroupPrice($product, $customerGroupId, $websiteId);
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getList($productSku)
+    {
+        $product = $this->productRepository->get($productSku, true);
+        $priceKey = 'website_price';
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) == 0) {
+            $priceKey = 'price';
+        }
+
+        $prices = array();
+        foreach ($product->getData('group_price') as $price) {
+            $this->groupPriceBuilder->populateWithArray(array(
+                Product\GroupPrice::CUSTOMER_GROUP_ID => $price['all_groups'] ? 'all' : $price['cust_group'],
+                Product\GroupPrice::VALUE => $price[$priceKey],
+            ));
+            $prices[] = $this->groupPriceBuilder->create();
+        }
+        return $prices;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/GroupPriceServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/GroupPriceServiceInterface.php
new file mode 100644
index 00000000000..fa8a40f8112
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/GroupPriceServiceInterface.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+interface GroupPriceServiceInterface
+{
+    /**
+     * Set group price for product
+     *
+     * @param string $productSku
+     * @param \Magento\Catalog\Service\V1\Data\Product\GroupPrice $price
+     * @return boolean
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function set($productSku, \Magento\Catalog\Service\V1\Data\Product\GroupPrice $price);
+
+    /**
+     * Remove group price from product
+     *
+     * @param string $productSku
+     * @param int $customerGroupId
+     * @return boolean
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function delete($productSku, $customerGroupId);
+
+    /**
+     * Retrieve list of product prices
+     *
+     * @param string $productSku
+     * @return \Magento\Catalog\Service\V1\Data\Product\GroupPrice[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function getList($productSku);
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/TierPriceService.php b/app/code/Magento/Catalog/Service/V1/Product/TierPriceService.php
new file mode 100644
index 00000000000..c45a24f22a6
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/TierPriceService.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+use Magento\Catalog\Model\ProductFactory;
+use Magento\Catalog\Model\ProductRepository;
+use Magento\Framework\Exception\InputException;
+use Magento\Framework\Exception\CouldNotSaveException;
+use Magento\Catalog\Service\V1\Data\Product;
+
+class TierPriceService implements TierPriceServiceInterface
+{
+    /**
+     * @var \Magento\Catalog\Model\ProductRepository
+     */
+    protected $productRepository;
+
+    /**
+     * @var \Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder
+     */
+    protected $priceBuilder;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface
+     */
+    protected $storeManager;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\PriceModifier
+     */
+    protected $priceModifier;
+
+    /**
+     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     */
+    protected $config;
+
+    /**
+     * @var \Magento\Customer\Service\V1\CustomerGroupServiceInterface
+     */
+    protected $customerGroupService;
+
+    /**
+     * @param ProductRepository $productRepository
+     * @param Product\TierPriceBuilder $priceBuilder
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param \Magento\Catalog\Model\Product\PriceModifier $priceModifier
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
+     * @param \Magento\Customer\Service\V1\CustomerGroupServiceInterface $customerGroupService
+     */
+    public function __construct(
+        ProductRepository $productRepository,
+        Product\TierPriceBuilder $priceBuilder,
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
+        \Magento\Catalog\Model\Product\PriceModifier $priceModifier,
+        \Magento\Framework\App\Config\ScopeConfigInterface $config,
+        \Magento\Customer\Service\V1\CustomerGroupServiceInterface $customerGroupService
+    ) {
+        $this->productRepository = $productRepository;
+        $this->priceBuilder = $priceBuilder;
+        $this->storeManager = $storeManager;
+        $this->priceModifier = $priceModifier;
+        $this->config = $config;
+        $this->customerGroupService = $customerGroupService;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    public function set($productSku, $customerGroupId, \Magento\Catalog\Service\V1\Data\Product\TierPrice $price)
+    {
+        $product = $this->productRepository->get($productSku, true);
+        $customerGroup = $this->customerGroupService->getGroup($customerGroupId);
+
+        $tierPrices = $product->getData('tier_price');
+        $websiteId = 0;
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) != 0) {
+            $websiteId = $this->storeManager->getWebsite()->getId();
+        }
+
+        $found = false;
+
+        foreach ($tierPrices as &$item) {
+            if ('all' == $customerGroupId) {
+                $isGroupValid = ($item['all_groups'] == 1);
+            } else {
+                $isGroupValid = ($item['cust_group'] == $customerGroupId);
+            }
+
+            if ($isGroupValid && $item['website_id'] == $websiteId && $item['price_qty'] == $price->getQty()) {
+                $item['price'] = $price->getValue();
+                $found = true;
+                break;
+            }
+        }
+        if (!$found) {
+            $mappedCustomerGroupId = 'all' == $customerGroupId
+                ? \Magento\Customer\Service\V1\CustomerGroupServiceInterface::CUST_GROUP_ALL
+                : $customerGroup->getId();
+
+            $tierPrices[] = array(
+                'cust_group' => $mappedCustomerGroupId,
+                'price' => $price->getValue(),
+                'website_price' => $price->getValue(),
+                'website_id' => $websiteId,
+                'price_qty' => $price->getQty()
+            );
+        }
+
+        $product->setData('tier_price', $tierPrices);
+        $errors = $product->validate();
+        if (is_array($errors) && count($errors)) {
+            $errorAttributeCodes = implode(', ', array_keys($errors));
+            throw new InputException(
+                sprintf('Values of following attributes are invalid: %s', $errorAttributeCodes)
+            );
+        }
+        try {
+            $product->save();
+        } catch (\Exception $e) {
+            throw new CouldNotSaveException('Could not save group price');
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete($productSku, $customerGroupId, $qty)
+    {
+        $product = $this->productRepository->get($productSku, true);
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) == 0) {
+            $websiteId = 0;
+        } else {
+            $websiteId = $this->storeManager->getWebsite()->getId();
+        }
+        $this->priceModifier->removeTierPrice($product, $customerGroupId, $qty, $websiteId);
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getList($productSku, $customerGroupId)
+    {
+        $product = $this->productRepository->get($productSku, true);
+
+        $priceKey = 'website_price';
+        if ($this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE) == 0) {
+            $priceKey = 'price';
+        }
+
+        $prices = array();
+        foreach ($product->getData('tier_price') as $price) {
+            if ((is_numeric($customerGroupId) && intval($price['cust_group']) === intval($customerGroupId))
+                || ($customerGroupId === 'all' && $price['all_groups'])
+            ) {
+                $this->priceBuilder->populateWithArray(array(
+                    Product\TierPrice::VALUE => $price[$priceKey],
+                    Product\TierPrice::QTY => $price['price_qty']
+                ));
+                $prices[] = $this->priceBuilder->create();
+            }
+        }
+        return $prices;
+    }
+}
diff --git a/app/code/Magento/Catalog/Service/V1/Product/TierPriceServiceInterface.php b/app/code/Magento/Catalog/Service/V1/Product/TierPriceServiceInterface.php
new file mode 100644
index 00000000000..83a941fdd5c
--- /dev/null
+++ b/app/code/Magento/Catalog/Service/V1/Product/TierPriceServiceInterface.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+interface TierPriceServiceInterface
+{
+    /**
+     * Create tire price for product
+     *
+     * @param string $productSku
+     * @param string $customerGroupId
+     * @param \Magento\Catalog\Service\V1\Data\Product\TierPrice $price
+     * @return boolean
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function set($productSku, $customerGroupId, \Magento\Catalog\Service\V1\Data\Product\TierPrice $price);
+
+    /**
+     * Remove tire price from product
+     *
+     * @param string $productSku
+     * @param string $customerGroupId
+     * @param float $qty
+     * @return boolean
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function delete($productSku, $customerGroupId, $qty);
+
+    /**
+     * Get tire price of product
+     *
+     * @param string $productSku
+     * @param string $customerGroupId
+     * @return \Magento\Catalog\Service\V1\Data\Product\TierPrice[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function getList($productSku, $customerGroupId);
+}
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 37365f8cb65..e38c660f426 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -42,9 +42,13 @@
     <preference for="Magento\Catalog\Service\V1\Product\AttributeSet\ReadServiceInterface" type="Magento\Catalog\Service\V1\Product\AttributeSet\ReadService" />
     <preference for="Magento\Catalog\Service\V1\Product\AttributeSet\WriteServiceInterface" type="Magento\Catalog\Service\V1\Product\AttributeSet\WriteService" />
     <preference for="Magento\Catalog\Service\V1\Product\AttributeSet\AttributeServiceInterface" type="Magento\Catalog\Service\V1\Product\AttributeSet\AttributeService" />
+    <preference for="Magento\Catalog\Service\V1\Product\Attribute\Media\ReadServiceInterface" type="\Magento\Catalog\Service\V1\Product\Attribute\Media\ReadService" />
+    <preference for="\Magento\Catalog\Service\V1\Product\Attribute\Media\WriteServiceInterface" type="\Magento\Catalog\Service\V1\Product\Attribute\Media\WriteService" />
     <preference for="Magento\Catalog\Service\V1\Product\Link\ReadServiceInterface" type="Magento\Catalog\Service\V1\Product\Link\ReadService" />
     <preference for="Magento\Catalog\Service\V1\Product\Link\WriteServiceInterface" type="Magento\Catalog\Service\V1\Product\Link\WriteService" />
     <preference for="Magento\Catalog\Service\V1\Product\Link\Data\ProductLink\DataMapperInterface" type="Magento\Catalog\Service\V1\Product\Link\Data\ProductLink\DataMapper\Composite" />
+    <preference for="Magento\Catalog\Service\V1\Product\GroupPriceServiceInterface" type="Magento\Catalog\Service\V1\Product\GroupPriceService" />
+    <preference for="Magento\Catalog\Service\V1\Product\TierPriceServiceInterface" type="Magento\Catalog\Service\V1\Product\TierPriceService" />
     <type name="Magento\Log\Model\Resource\Log">
         <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
     </type>
diff --git a/app/code/Magento/Catalog/etc/webapi.xml b/app/code/Magento/Catalog/etc/webapi.xml
index cd647e5e9e7..aa3c9ec05ca 100644
--- a/app/code/Magento/Catalog/etc/webapi.xml
+++ b/app/code/Magento/Catalog/etc/webapi.xml
@@ -181,4 +181,76 @@
             <resource ref="Magento_Catalog::catalog"/>
         </resources>
     </route>
+    <route url="/V1/products/media/types/:attributeSetId" method="GET">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\ReadServiceInterface" method="types"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/media/:imageId" method="GET">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\ReadServiceInterface" method="info"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/media" method="POST">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\WriteServiceInterface" method="create"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/media" method="PUT">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\WriteServiceInterface" method="update"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/media/:entryId" method="DELETE">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\WriteServiceInterface" method="delete"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/media" method="GET">
+        <service class="Magento\Catalog\Service\V1\Product\Attribute\Media\ReadServiceInterface" method="getList"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices" method="GET">
+        <service class="Magento\Catalog\Service\V1\Product\GroupPriceServiceInterface" method="getList"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices" method="POST">
+        <service class="Magento\Catalog\Service\V1\Product\GroupPriceServiceInterface" method="set"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices/:customerGroupId" method="DELETE">
+        <service class="Magento\Catalog\Service\V1\Product\GroupPriceServiceInterface" method="delete"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices/:customerGroupId/tiers" method="GET">
+        <service class="Magento\Catalog\Service\V1\Product\TierPriceServiceInterface" method="getList"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices/:customerGroupId/tiers" method="POST">
+        <service class="Magento\Catalog\Service\V1\Product\TierPriceServiceInterface" method="set"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/:productSku/group-prices/:customerGroupId/tiers/:qty" method="DELETE">
+        <service class="Magento\Catalog\Service\V1\Product\TierPriceServiceInterface" method="delete"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
 </routes>
diff --git a/app/code/Magento/Eav/Model/Resource/Attribute/Collection.php b/app/code/Magento/Eav/Model/Resource/Attribute/Collection.php
index 1cc178682cb..c924148a9e1 100644
--- a/app/code/Magento/Eav/Model/Resource/Attribute/Collection.php
+++ b/app/code/Magento/Eav/Model/Resource/Attribute/Collection.php
@@ -201,19 +201,19 @@ abstract class Collection extends \Magento\Eav\Model\Resource\Entity\Attribute\C
                 $scopeColumns['scope_website_id'] = $columnName;
             } else {
                 if (isset($mainColumns[$columnName])) {
-                    $alias = sprintf('scope_%s', $columnName);
-                    $expression = $connection->getCheckSql('main_table.%s IS NULL', 'scope_table.%s', 'main_table.%s');
-                    $expression = sprintf($expression, $columnName, $columnName, $columnName);
+                    $alias = 'scope_' . $columnName;
+                    $condition = 'main_table.' . $columnName . ' IS NULL';
+                    $true = 'scope_table.' . $columnName;
+                    $false = 'main_table.' . $columnName;
+                    $expression = $connection->getCheckSql($condition, $true, $false);
                     $this->addFilterToMap($columnName, $expression);
                     $scopeColumns[$alias] = $columnName;
                 } elseif (isset($extraColumns[$columnName])) {
-                    $alias = sprintf('scope_%s', $columnName);
-                    $expression = $connection->getCheckSql(
-                        'additional_table.%s IS NULL',
-                        'scope_table.%s',
-                        'additional_table.%s'
-                    );
-                    $expression = sprintf($expression, $columnName, $columnName, $columnName);
+                    $alias = 'scope_' . $columnName;
+                    $condition = 'additional_table.' . $columnName . ' IS NULL';
+                    $true = 'scope_table.' . $columnName;
+                    $false = 'additional_table.' . $columnName;
+                    $expression = $connection->getCheckSql($condition, $true, $false);
                     $this->addFilterToMap($columnName, $expression);
                     $scopeColumns[$alias] = $columnName;
                 }
diff --git a/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml b/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml
index a1f0c463602..aad1419c882 100644
--- a/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml
+++ b/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml
@@ -31,16 +31,19 @@ $minProduct = $this->getSaleableItem()
     ->getPriceInfo()
     ->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)
     ->getMinProduct();
-$amountRender = $this->getRendererPool()
-    ->createAmountRender(
-        $minProduct->getPriceInfo()->getPrice('final_price')->getAmount(),
-        $minProduct,
-        $minProduct->getPriceInfo()->getPrice('final_price'),
-        []
-    );
+
+if ($minProduct) {
+    $amountRender = $this->getRendererPool()
+        ->createAmountRender(
+            $minProduct->getPriceInfo()->getPrice('final_price')->getAmount(),
+            $minProduct,
+            $minProduct->getPriceInfo()->getPrice('final_price'),
+            []
+        );
+}
 ?>
 <div class="price-box" itemprop="offers" itemscope itemtype="http://schema.org/Offer">
-    <?php if (\Magento\Framework\Pricing\Render::ZONE_ITEM_VIEW != $this->getZone()): ?>
+    <?php if ($minProduct && \Magento\Framework\Pricing\Render::ZONE_ITEM_VIEW != $this->getZone()): ?>
     <p class="minimal-price">
         <span class="price-label"><?php echo __('Starting at:') . $amountRender->toHtml();?></span>
     </p>
diff --git a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml
index 25c562f3174..c68215b765f 100644
--- a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml
+++ b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml
@@ -68,10 +68,6 @@
                 <td class="col price">
                     <?php if ($this->getCanShowProductPrice($_item)): ?>
                     <?php echo $this->getProductPrice($_item) ?>
-                        <?php echo $this->getProductPriceHtml(
-                            $_item,
-                            \Magento\Catalog\Pricing\Price\TierPrice::PRICE_CODE
-                        ) ?>
                     <?php endif; ?>
                 </td>
                 <?php endif; ?>
diff --git a/app/code/Magento/Sales/Model/Quote/Item/AbstractItem.php b/app/code/Magento/Sales/Model/Quote/Item/AbstractItem.php
index c3511a5c797..b37c9dd59c8 100644
--- a/app/code/Magento/Sales/Model/Quote/Item/AbstractItem.php
+++ b/app/code/Magento/Sales/Model/Quote/Item/AbstractItem.php
@@ -403,7 +403,7 @@ abstract class AbstractItem extends \Magento\Framework\Model\AbstractModel imple
         $qty = $this->getTotalQty();
         // Round unit price before multiplying to prevent losing 1 cent on subtotal
         $total = $this->getStore()->roundPrice($this->getCalculationPriceOriginal()) * $qty;
-        $baseTotal = $this->getBaseCalculationPriceOriginal() * $qty;
+        $baseTotal = $this->getStore()->roundPrice($this->getBaseCalculationPriceOriginal()) * $qty;
 
         $this->setRowTotal($this->getStore()->roundPrice($total));
         $this->setBaseRowTotal($this->getStore()->roundPrice($baseTotal));
diff --git a/app/code/Magento/SalesRule/Model/Validator.php b/app/code/Magento/SalesRule/Model/Validator.php
index 25f7269da75..12367e76dc2 100644
--- a/app/code/Magento/SalesRule/Model/Validator.php
+++ b/app/code/Magento/SalesRule/Model/Validator.php
@@ -173,7 +173,8 @@ class Validator extends \Magento\Framework\Model\AbstractModel
                 $websiteId,
                 $customerGroupId,
                 $couponCode
-            )->load();
+            )->addFieldToFilter('is_active', 1)
+            ->load();
         }
         return $this;
     }
diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
index dc44e5abfd0..b5c6352852a 100644
--- a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
+++ b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
@@ -164,6 +164,20 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
             false,
             true
         );
+        
+        $fieldset->addField(
+            'calculate_subtotal',
+            'checkbox',
+            array(
+                'name'  => 'calculate_subtotal',
+                'label' => __('Calculate Off Subtotal Only'),
+                'onclick' => 'this.value = this.checked ? 1 : 0;',
+                'checked' => (int)$model->getCalculateSubtotal()
+            ),
+            false,
+            true
+        );
+
         $fieldset->addField(
             'position',
             'text',
diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php
index 911ab864539..49c4d799852 100644
--- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php
+++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php
@@ -148,14 +148,24 @@ class Tax extends \Magento\Framework\View\Element\Template
             $subtotalIncl = (double)$this->_source->getSubtotalInclTax();
             $baseSubtotalIncl = (double)$this->_source->getBaseSubtotalInclTax();
 
-            if (!$subtotalIncl) {
-                $subtotalIncl = $subtotal + $this->_source->getTaxAmount() - $this->_source->getShippingTaxAmount();
-            }
-            if (!$baseSubtotalIncl) {
-                $baseSubtotalIncl = $baseSubtotal +
-                    $this->_source->getBaseTaxAmount() -
-                    $this->_source->getBaseShippingTaxAmount();
+            if (!$subtotalIncl || !$baseSubtotalIncl) {
+                // Calculate the subtotal if it is not set
+                $subtotalIncl = $subtotal
+                    + $this->_source->getTaxAmount()
+                    - $this->_source->getShippingTaxAmount();
+                $baseSubtotalIncl = $baseSubtotal
+                    + $this->_source->getBaseTaxAmount()
+                    - $this->_source->getBaseShippingTaxAmount();
+
+                if ($this->_source instanceof Order) {
+                    // Adjust for the discount tax compensation
+                    foreach ($this->_source->getAllItems() as $item) {
+                        $subtotalIncl += $item->getHiddenTaxAmount();
+                        $baseSubtotalIncl += $item->getBaseHiddenTaxAmount();
+                    }
+                }
             }
+
             $subtotalIncl = max(0, $subtotalIncl);
             $baseSubtotalIncl = max(0, $baseSubtotalIncl);
             $totalExcl = new \Magento\Framework\Object(
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rule.php b/app/code/Magento/Tax/Controller/Adminhtml/Rule.php
index 05f5d442f0c..9de72cd297d 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Rule.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Rule.php
@@ -51,6 +51,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Index action
+     *
      * @return $this
      */
     public function indexAction()
@@ -63,6 +65,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Redirects to edit action
+     *
      * @return void
      */
     public function newAction()
@@ -71,6 +75,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Edit action
+     *
      * @return void
      */
     public function editAction()
@@ -106,6 +112,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Save action
+     *
      * @return void
      */
     public function saveAction()
@@ -115,6 +123,7 @@ class Rule extends \Magento\Backend\App\Action
 
             $ruleModel = $this->_objectManager->get('Magento\Tax\Model\Calculation\Rule');
             $ruleModel->setData($postData);
+            $ruleModel->setCalculateSubtotal($this->getRequest()->getParam('calculate_subtotal', 0));
 
             try {
                 $ruleModel->save();
@@ -142,6 +151,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Delete action
+     *
      * @return void
      */
     public function deleteAction()
@@ -191,6 +202,8 @@ class Rule extends \Magento\Backend\App\Action
     }
 
     /**
+     * Check if sales rule is allowed
+     *
      * @return bool
      */
     protected function _isAllowed()
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Tax.php b/app/code/Magento/Tax/Controller/Adminhtml/Tax.php
index 9d89ee15c1a..4286cf94cec 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Tax.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Tax.php
@@ -155,4 +155,24 @@ class Tax extends \Magento\Backend\App\Action
     {
         return $this->_authorization->isAllowed('Magento_Tax::manage_tax');
     }
+
+    /**
+     * Set tax ignore notification flag and redirect back
+     *
+     * @return \Magento\Framework\App\ResponseInterface
+     */
+    public function ignoreTaxNotificationAction()
+    {
+        $section = $this->getRequest()->getParam('section');
+        if ($section) {
+            try {
+                $path = 'tax/notification/ignore_' . $section;
+                $this->_objectManager->get('\Magento\Core\Model\Resource\Config')->saveConfig($path, 1, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0);
+            } catch (Exception $e) {
+                $this->messageManager->addError($e->getMessage());
+            }
+        }
+
+        $this->getResponse()->setRedirect($this->_redirect->getRefererUrl());
+    }
 }
diff --git a/app/code/Magento/Tax/Helper/Data.php b/app/code/Magento/Tax/Helper/Data.php
index c3c4371f528..60a39020746 100644
--- a/app/code/Magento/Tax/Helper/Data.php
+++ b/app/code/Magento/Tax/Helper/Data.php
@@ -25,6 +25,7 @@ namespace Magento\Tax\Helper;
 
 use Magento\Store\Model\Store;
 use Magento\Customer\Model\Address;
+use Magento\Tax\Model\Calculation;
 use Magento\Tax\Model\Config;
 
 /**
@@ -481,6 +482,42 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
         return $this->_coreData->jsonEncode($result);
     }
 
+    /**
+     * Get unrounded product price
+     *
+     * @param   \Magento\Catalog\Model\Product $product
+     * @param   float $price inputed product price
+     * @param   bool $includingTax return price include tax flag
+     * @param   null|Address $shippingAddress
+     * @param   null|Address $billingAddress
+     * @param   null|int $ctc customer tax class
+     * @param   null|string|bool|int|Store $store
+     * @param   bool $priceIncludesTax flag what price parameter contain tax
+     * @return  float
+     */
+    public function getPriceUnrounded(
+        $product,
+        $price,
+        $includingTax = null,
+        $shippingAddress = null,
+        $billingAddress = null,
+        $ctc = null,
+        $store = null,
+        $priceIncludesTax = null
+    ) {
+        return $this->getPrice(
+            $product,
+            $price,
+            $includingTax,
+            $shippingAddress,
+            $billingAddress,
+            $ctc,
+            $store,
+            $priceIncludesTax,
+            false
+        );
+    }
+
     /**
      * Get product price with all tax settings processing
      *
diff --git a/app/code/Magento/Tax/Model/Config.php b/app/code/Magento/Tax/Model/Config.php
index 83aae6075cf..cf0b53ae8f7 100644
--- a/app/code/Magento/Tax/Model/Config.php
+++ b/app/code/Magento/Tax/Model/Config.php
@@ -33,6 +33,15 @@ use Magento\Store\Model\Store;
 
 class Config
 {
+    // tax notifications
+    const XML_PATH_TAX_NOTIFICATION_IGNORE_DISCOUNT = 'tax/notification/ignore_discount';
+
+    const XML_PATH_TAX_NOTIFICATION_IGNORE_PRICE_DISPLAY = 'tax/notification/ignore_price_display';
+
+    const XML_PATH_TAX_NOTIFICATION_IGNORE_FPT_CONFIGURATION = 'tax/notification/ignore_fpt_configuration';
+
+    const XML_PATH_TAX_NOTIFICATION_INFO_URL = 'tax/notification/info_url';
+
     // tax classes
     const CONFIG_XML_PATH_SHIPPING_TAX_CLASS = 'tax/classes/shipping_tax_class';
 
@@ -683,7 +692,7 @@ class Config
      * @param null|string|bool|int|Store $store
      * @return bool
      */
-    public function displaySalestDiscountExclTax($store = null)
+    public function displaySalesDiscountExclTax($store = null)
     {
         return $this->_scopeConfig->getValue(
             self::XML_PATH_DISPLAY_SALES_DISCOUNT,
@@ -758,4 +767,64 @@ class Config
             $store
         );
     }
+
+    /**
+     * Check if do not show notification about wrong display settings
+     *
+     * @param null|string|bool|int|Store $store
+     * @return bool
+     */
+    public function isWrongDisplaySettingsIgnored($store = null)
+    {
+        return (bool)$this->_scopeConfig->getValue(
+            self::XML_PATH_TAX_NOTIFICATION_IGNORE_PRICE_DISPLAY,
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+            $store
+        );
+    }
+
+    /**
+     * Check if do not show notification about wrong discount settings
+     *
+     * @param null|string|bool|int|Store $store
+     * @return bool
+     */
+    public function isWrongDiscountSettingsIgnored($store = null)
+    {
+        return (bool)$this->_scopeConfig->getValue(
+            self::XML_PATH_TAX_NOTIFICATION_IGNORE_DISCOUNT,
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+            $store
+        );
+    }
+
+    /**
+     * Check if warning about conflicting FPT configuration should be shown
+     *
+     * @param null|string|bool|int|Store $store
+     * @return bool
+     */
+    public function isConflictingFptTaxConfigurationSettingsIgnored($store = null)
+    {
+        return (bool)$this->_scopeConfig->getValue(
+            self::XML_PATH_TAX_NOTIFICATION_IGNORE_FPT_CONFIGURATION,
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+            $store
+        );
+    }
+
+    /**
+     * Return the notification info url
+     *
+     * @param null|string|bool|int|Store $store
+     * @return bool
+     */
+    public function getInfoUrl($store = null)
+    {
+        return (bool)$this->_scopeConfig->getValue(
+            self::XML_PATH_TAX_NOTIFICATION_INFO_URL,
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+            $store
+        );
+    }
 }
diff --git a/app/code/Magento/Tax/Model/Config/Notification.php b/app/code/Magento/Tax/Model/Config/Notification.php
new file mode 100644
index 00000000000..0c4805c6ab3
--- /dev/null
+++ b/app/code/Magento/Tax/Model/Config/Notification.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Tax\Model\Config;
+
+/**
+ * Tax Config Notification
+ */
+class Notification extends \Magento\Framework\App\Config\Value
+{
+    /**
+     * @var \Magento\Core\Model\Resource\Config
+     */
+    protected $resourceConfig;
+
+    /**
+     * @param \Magento\Framework\Model\Context $context
+     * @param \Magento\Framework\Registry $registry
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
+     * @param \Magento\Core\Model\Resource\Config $resourceConfig
+     * @param \Magento\Framework\Model\Resource\AbstractResource $resource
+     * @param \Magento\Framework\Data\Collection\Db $resourceCollection
+     * @param array $data
+     */
+    public function __construct(
+        \Magento\Framework\Model\Context $context,
+        \Magento\Framework\Registry $registry,
+        \Magento\Framework\App\Config\ScopeConfigInterface $config,
+        \Magento\Core\Model\Resource\Config $resourceConfig,
+        \Magento\Framework\Model\Resource\AbstractResource $resource = null,
+        \Magento\Framework\Data\Collection\Db $resourceCollection = null,
+        array $data = array()
+    ) {
+        $this->resourceConfig = $resourceConfig;
+        parent::__construct($context, $registry, $config, $resource, $resourceCollection, $data);
+    }
+
+    /**
+     * Prepare and store cron settings after save
+     *
+     * @return \Magento\Tax\Model\Config\Notification
+     */
+    protected function _afterSave()
+    {
+        if ($this->isValueChanged()) {
+            $this->_resetNotificationFlag(\Magento\Tax\Model\Config::XML_PATH_TAX_NOTIFICATION_IGNORE_DISCOUNT);
+            $this->_resetNotificationFlag(\Magento\Tax\Model\Config::XML_PATH_TAX_NOTIFICATION_IGNORE_PRICE_DISPLAY);
+        }
+        return parent::_afterSave($this);
+    }
+
+    /**
+     * Reset flag for showing tax notifications
+     *
+     * @param string $path
+     * @return \Magento\Tax\Model\Config\Notification
+     */
+    protected function _resetNotificationFlag($path)
+    {
+        $this->resourceConfig->saveConfig($path, 0, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0);
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
index fc1080efb46..87a69428140 100644
--- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
+++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
@@ -933,7 +933,6 @@ class Tax extends AbstractTotal
         $rateKey = ($taxId == null) ? (string)$rate : $taxId;
         $taxSubtotal = $subtotal = $item->getTaxableAmount() + $item->getExtraRowTaxableAmount();
         $baseTaxSubtotal = $baseSubtotal = $item->getBaseTaxableAmount() + $item->getBaseExtraRowTaxableAmount();
-        $item->setTaxPercent($rate);
 
         if (!isset($taxGroups[$rateKey]['totals'])) {
             $taxGroups[$rateKey]['totals'] = array();
diff --git a/app/code/Magento/Tax/Model/System/Message/Notifications.php b/app/code/Magento/Tax/Model/System/Message/Notifications.php
new file mode 100644
index 00000000000..4e0d52ef063
--- /dev/null
+++ b/app/code/Magento/Tax/Model/System/Message/Notifications.php
@@ -0,0 +1,337 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Tax\Model\System\Message;
+
+/**
+ * Notifications class
+ */
+class Notifications implements \Magento\AdminNotification\Model\System\MessageInterface
+{
+    /**
+     * Store manager object
+     *
+     * @var \Magento\Store\Model\StoreManagerInterface
+     */
+    protected $storeManager;
+
+    /**
+     * @var \Magento\Framework\UrlInterface
+     */
+    protected $urlBuilder;
+
+    /**
+     * Tax configuration object
+     *
+     * @var \Magento\Tax\Model\Config
+     */
+    protected $taxConfig;
+
+    /*
+     * Stores with invalid display settings
+     *
+     * @var array
+     */
+    protected $storesWithInvalidDisplaySettings;
+
+    /*
+     * Websites with invalid discount settings
+     *
+     * @var array
+     */
+    protected $storesWithInvalidDiscountSettings;
+
+    /*
+     * Stores with conflicting FPT settings
+     *
+     * @var array
+     */
+    protected $storesWithConflictingFPTSettings;
+
+    /**
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     * @param \Magento\Framework\UrlInterface $urlBuilder
+     * @param \Magento\Tax\Model\Config $taxConfig
+     */
+    public function __construct(
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
+        \Magento\Framework\UrlInterface $urlBuilder,
+        \Magento\Tax\Model\Config $taxConfig
+    ) {
+        $this->storeManager = $storeManager;
+        $this->urlBuilder = $urlBuilder;
+        $this->taxConfig = $taxConfig;
+    }
+
+    /**
+     * Retrieve unique message identity
+     *
+     * @return string
+     */
+    public function getIdentity()
+    {
+        return md5('TAX_NOTIFICATION');
+    }
+
+    /**
+     * Check if tax calculation type and price display settings are compatible
+     *
+     * Invalid settings if
+     *      Tax Calculation Method Based On 'Total' or 'Row'
+     *      and at least one Price Display Settings has 'Including and Excluding Tax' value
+     *
+     * @param null|int|bool|string|Store $store $store
+     * @return bool
+     */
+    public function checkDisplaySettings($store = null)
+    {
+        if ($this->taxConfig->getAlgorithm($store) == \Magento\Tax\Model\Calculation::CALC_UNIT_BASE) {
+            return true;
+        }
+        return $this->taxConfig->getPriceDisplayType($store) != \Magento\Tax\Model\Config::DISPLAY_TYPE_BOTH
+        && $this->taxConfig->getShippingPriceDisplayType($store) != \Magento\Tax\Model\Config::DISPLAY_TYPE_BOTH
+        && !$this->taxConfig->displayCartPricesBoth($store)
+        && !$this->taxConfig->displayCartSubtotalBoth($store)
+        && !$this->taxConfig->displayCartShippingBoth($store)
+        && !$this->taxConfig->displaySalesPricesBoth($store)
+        && !$this->taxConfig->displaySalesSubtotalBoth($store)
+        && !$this->taxConfig->displaySalesShippingBoth($store);
+    }
+
+    /**
+     * Check if tax discount settings are compatible
+     *
+     * Matrix for invalid discount settings is as follows:
+     *      Before Discount / Excluding Tax
+     *      Before Discount / Including Tax
+     *
+     * @param null|int|bool|string|Store $store $store
+     * @return bool
+     */
+    public function checkDiscountSettings($store = null)
+    {
+        return $this->taxConfig->applyTaxAfterDiscount($store);
+    }
+
+    /**
+     * Get URL for the tax notification documentation
+     *
+     * @return string
+     */
+    public function getInfoUrl()
+    {
+        return $this->taxConfig->getInfoUrl();
+    }
+
+    /**
+     * Get URL to the admin tax configuration page
+     *
+     * @return string
+     */
+    public function getManageUrl()
+    {
+        return $this->urlBuilder->getUrl('adminhtml/system_config/edit/section/tax');
+    }
+
+    /**
+     * Get URL to ignore tax notifications
+     *
+     * @param string $section
+     * @return string
+     */
+    public function getIgnoreTaxNotificationUrl($section)
+    {
+        return $this->urlBuilder->getUrl('tax/tax/ignoreTaxNotification', array('section' => $section));
+    }
+
+    /**
+     * Return list of store names which have not compatible tax calculation type and price display settings.
+     * Return true if settings are wrong for default store.
+     *
+     * @return array
+     */
+    public function getStoresWithWrongDisplaySettings()
+    {
+        $storeNames = array();
+        $storeCollection = $this->storeManager->getStores(true);
+        foreach ($storeCollection as $store) {
+            if (!$this->checkDisplaySettings($store)) {
+                $website = $store->getWebsite();
+                $storeNames[] = $website->getName() . '(' . $store->getName() . ')';
+            }
+        }
+        return $storeNames;
+    }
+
+    /**
+     * Return list of store names where tax discount settings are compatible.
+     * Return true if settings are wrong for default store.
+     *
+     * @return array
+     */
+    public function getStoresWithWrongDiscountSettings()
+    {
+        $storeNames = array();
+        $storeCollection = $this->storeManager->getStores(true);
+        foreach ($storeCollection as $store) {
+            if (!$this->checkDiscountSettings($store)) {
+                $website = $store->getWebsite();
+                $storeNames[] = $website->getName() . '(' . $store->getName() . ')';
+            }
+        }
+        return $storeNames;
+    }
+
+    /**
+     * Return list of store names which have not compatible tax calculation type and price display settings.
+     * Return true if settings are wrong for default store.
+     *
+     * @return array
+     */
+    public function getStoresWithConflictingFptTaxConfigurationSettings()
+    {
+        $storeNames = array();
+
+        // Will enable in future work
+        //$storeCollection = $this->storeManager->getStores(true);
+        //foreach ($storeCollection as $store) {
+        //    if ($this->weeeData->validateCatalogPricesAndFptConfiguration($store)) {
+        //        $website = $store->getWebsite();
+        //        $storeNames[] = $website->getName() . '(' . $store->getName() . ')';
+        //    }
+        //}
+
+        return $storeNames;
+    }
+
+    /**
+     * Check whether notification is displayed
+     * Checks if any of these settings are being ignored or valid:
+     *      1. Wrong discount settings
+     *      2. Wrong display settings
+     *      3. Conflicting FPT settings
+     *
+     * @return bool
+     */
+    public function isDisplayed()
+    {
+        // Check if we are ignoring all notifications
+        if ($this->taxConfig->isWrongDisplaySettingsIgnored() && $this->taxConfig->isWrongDiscountSettingsIgnored()
+            && $this->taxConfig->isConflictingFptTaxConfigurationSettingsIgnored()) {
+            return false;
+        }
+
+        $this->storesWithInvalidDisplaySettings = $this->getStoresWithWrongDisplaySettings();
+        $this->storesWithInvalidDiscountSettings = $this->getStoresWithWrongDiscountSettings();
+        $this->storesWithConflictingFPTSettings = $this->getStoresWithConflictingFptTaxConfigurationSettings();
+
+        // Check if we have valid tax notifications
+        if ((!empty($this->storesWithInvalidDisplaySettings) && !$this->taxConfig->isWrongDisplaySettingsIgnored())
+            || (!empty($this->storesWithInvalidDiscountSettings) && !$this->taxConfig->isWrongDiscountSettingsIgnored())
+            || (!empty($this->storesWithConflictingFPTSettings)
+                && !$this->taxConfig->isConflictingFptTaxConfigurationSettingsIgnored())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Build message text
+     * Determine which notification and data to display
+     *
+     * @return string
+     */
+    public function getText()
+    {
+        $messageDetails = '';
+
+        if (!empty($this->storesWithInvalidDisplaySettings) && !$this->taxConfig->isWrongDisplaySettingsIgnored()) {
+            $messageDetails .= '<strong>';
+            $messageDetails .= __('Warning tax configuration can result in rounding errors. ');
+            $messageDetails .= '</strong><br>';
+            $messageDetails .= __('Store(s) affected: ');
+            $messageDetails .= implode(', ', $this->storesWithInvalidDisplaySettings);
+            $messageDetails .= '<br><div style="text-align:right">';
+            $messageDetails .= __(
+                'Click on the link to <a href="%1">ignore this notification</a>',
+                $this->getIgnoreTaxNotificationUrl('price_display')
+            );
+            $messageDetails .= "</div><br>";
+        }
+
+        if (!empty($this->storesWithInvalidDiscountSettings) && !$this->taxConfig->isWrongDiscountSettingsIgnored()) {
+            $messageDetails .= '<strong>';
+            $messageDetails .= __(
+                'Warning tax discount configuration might result in different discounts
+                                than a customer might expect. '
+            );
+            $messageDetails .= '</strong><br>';
+            $messageDetails .= __('Store(s) affected: ');
+            $messageDetails .= implode(', ', $this->storesWithInvalidDiscountSettings);
+            $messageDetails .= '<br><div style="text-align:right">';
+            $messageDetails .= __(
+                'Click on the link to <a href="%1">ignore this notification</a>',
+                $this->getIgnoreTaxNotificationUrl('discount')
+            );
+            $messageDetails .= "</div><br>";
+        }
+
+        if (!empty($this->storesWithConflictingFPTSettings)
+            && !$this->taxConfig->isConflictingFptTaxConfigurationSettingsIgnored()
+        ) {
+            $messageDetails .= '<strong>';
+            $messageDetails .= __(
+                'Warning tax configuration can result in unexpected FPT prices on applicable devices. '
+            );
+            $messageDetails .= '</strong><br>';
+            $messageDetails .= __('Store(s) affected: ');
+            $messageDetails .= implode(', ', $this->storesWithConflictingFPTSettings);
+            $messageDetails .= '<br><div style="text-align:right">';
+            $messageDetails .= __(
+                'Click on the link to <a href="%1">ignore this notification</a>',
+                $this->getIgnoreTaxNotificationUrl('fpt_configuration')
+            );
+            $messageDetails .= "</div><br>";
+        }
+
+        $messageDetails .= '<br>';
+        $messageDetails .= __('Please see <a href="%1">documentation</a> for more details. ', $this->getInfoUrl());
+        $messageDetails .= __(
+            'Click here to go to <a href="%1">Tax Configuration</a> and change your settings.',
+            $this->getManageUrl()
+        );
+
+        return $messageDetails;
+    }
+
+    /**
+     * Retrieve message severity
+     *
+     * @return int
+     */
+    public function getSeverity()
+    {
+        return self::SEVERITY_CRITICAL;
+    }
+}
diff --git a/app/code/Magento/Tax/Pricing/Adjustment.php b/app/code/Magento/Tax/Pricing/Adjustment.php
index daa43ca113e..edd8467edf5 100644
--- a/app/code/Magento/Tax/Pricing/Adjustment.php
+++ b/app/code/Magento/Tax/Pricing/Adjustment.php
@@ -98,7 +98,7 @@ class Adjustment implements AdjustmentInterface
     public function extractAdjustment($amount, SaleableInterface $saleableItem)
     {
         if ($this->taxHelper->priceIncludesTax()) {
-            $adjustedAmount = $this->taxHelper->getPrice($saleableItem, $amount);
+            $adjustedAmount = $this->taxHelper->getPriceUnrounded($saleableItem, $amount);
             $result = $amount - $adjustedAmount;
         } else {
             $result = 0.;
@@ -116,7 +116,7 @@ class Adjustment implements AdjustmentInterface
     public function applyAdjustment($amount, SaleableInterface $saleableItem)
     {
         $includingTax = !$this->taxHelper->priceIncludesTax();
-        return $this->taxHelper->getPrice($saleableItem, $amount, $includingTax);
+        return $this->taxHelper->getPriceUnrounded($saleableItem, $amount, $includingTax);
     }
 
     /**
diff --git a/app/code/Magento/Tax/etc/adminhtml/di.xml b/app/code/Magento/Tax/etc/adminhtml/di.xml
new file mode 100644
index 00000000000..16e2bb8fa3c
--- /dev/null
+++ b/app/code/Magento/Tax/etc/adminhtml/di.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Academic Free License (AFL 3.0)
+ * that is bundled with this package in the file LICENSE_AFL.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/afl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
+    <type name="Magento\AdminNotification\Model\System\MessageList">
+        <arguments>
+            <argument name="messages" xsi:type="array">
+                <item name="tax" xsi:type="string">Magento\Tax\Model\System\Message\Notifications</item>
+            </argument>
+        </arguments>
+    </type>
+</config>
\ No newline at end of file
diff --git a/app/code/Magento/Tax/etc/adminhtml/system.xml b/app/code/Magento/Tax/etc/adminhtml/system.xml
index 9c1ec0e5e21..0e240d0de7b 100644
--- a/app/code/Magento/Tax/etc/adminhtml/system.xml
+++ b/app/code/Magento/Tax/etc/adminhtml/system.xml
@@ -54,6 +54,7 @@
                 <field id="based_on" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Tax Calculation Based On</label>
                     <source_model>Magento\Tax\Model\Config\Source\Basedon</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="price_includes_tax" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Catalog Prices</label>
@@ -70,16 +71,23 @@
                 <field id="apply_after_discount" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Apply Customer Tax</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Apply</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="discount_tax" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Apply Discount On Prices</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                     <comment>Apply discount on price including tax is calculated based on store tax, if "Apply Tax after Discount" is selected.</comment>
                 </field>
                 <field id="apply_tax_on" translate="label comment" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Apply Tax On</label>
                     <source_model>Magento\Tax\Model\Config\Source\Apply\On</source_model>
                 </field>
+                <field id="cross_border_trade_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0">
+                    <label>Enable Cross Border Trade</label>
+                    <source_model>Magento\Backend\Model\Config\Source\Yesno</source_model>
+                    <comment>When catalog price includes tax, enable this setting will fix the price no matter what the customer's tax rate is.</comment>
+                </field>
             </group>
             <group id="defaults" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                 <label>Default Tax Destination Calculation</label>
@@ -101,10 +109,12 @@
                 <field id="type" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Product Prices In Catalog</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="shipping" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Shipping Prices</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
             </group>
             <group id="cart_display" translate="label" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
@@ -112,14 +122,17 @@
                 <field id="price" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Prices</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="subtotal" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Subtotal</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="shipping" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Shipping Amount</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="grandtotal" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Include Tax In Grand Total</label>
@@ -139,14 +152,17 @@
                 <field id="price" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Prices</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="subtotal" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Subtotal</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="shipping" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Display Shipping Amount</label>
                     <source_model>Magento\Tax\Model\System\Config\Source\Tax\Display\Type</source_model>
+                    <backend_model>Magento\Tax\Model\Config\Notification</backend_model>
                 </field>
                 <field id="grandtotal" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Include Tax In Grand Total</label>
diff --git a/app/code/Magento/Tax/etc/config.xml b/app/code/Magento/Tax/etc/config.xml
index 9df3ec19f8d..9d4ef023802 100644
--- a/app/code/Magento/Tax/etc/config.xml
+++ b/app/code/Magento/Tax/etc/config.xml
@@ -31,7 +31,7 @@
             </classes>
             <calculation>
                 <algorithm>TOTAL_BASE_CALCULATION</algorithm>
-                <apply_after_discount>0</apply_after_discount>
+                <apply_after_discount>1</apply_after_discount>
                 <discount_tax>0</discount_tax>
                 <based_on>shipping</based_on>
                 <price_includes_tax>0</price_includes_tax>
@@ -66,6 +66,9 @@
                 <full_summary>0</full_summary>
                 <zero_tax>0</zero_tax>
             </sales_display>
+            <notification>
+                <url>http://www.magentocommerce.com/knowledge-base/entry/magento-ce-18-ee-113-tax-calc</url>
+            </notification>
         </tax>
     </default>
 </config>
diff --git a/app/code/Magento/Tax/etc/module.xml b/app/code/Magento/Tax/etc/module.xml
index 39c8385237c..4a737678df7 100644
--- a/app/code/Magento/Tax/etc/module.xml
+++ b/app/code/Magento/Tax/etc/module.xml
@@ -44,6 +44,7 @@
             <module name="Magento_Reports"/>
             <module name="Magento_Theme"/>
             <module name="Magento_ConfigurableProduct"/>
+            <module name="Magento_AdminNotification"/>
         </depends>
     </module>
 </config>
diff --git a/app/code/Magento/Tax/view/adminhtml/layout/tax_rule_block.xml b/app/code/Magento/Tax/view/adminhtml/layout/tax_rule_block.xml
index c64f797803d..56f0be48011 100644
--- a/app/code/Magento/Tax/view/adminhtml/layout/tax_rule_block.xml
+++ b/app/code/Magento/Tax/view/adminhtml/layout/tax_rule_block.xml
@@ -94,6 +94,13 @@
                         <argument name="type" xsi:type="string">text</argument>
                     </arguments>
                 </block>
+                <block class="Magento\Backend\Block\Widget\Grid\Column" as="calculate_subtotal">
+                    <arguments>
+                        <argument name="header" xsi:type="string" translate="true">Subtotal Only</argument>
+                        <argument name="index" xsi:type="string">calculate_subtotal</argument>
+                        <argument name="type" xsi:type="string">text</argument>
+                    </arguments>
+                </block>
                 <block class="Magento\Backend\Block\Widget\Grid\Column" as="position">
                     <arguments>
                         <argument name="header" xsi:type="string" translate="true">Sort Order</argument>
diff --git a/app/code/Magento/Weee/Model/Tax.php b/app/code/Magento/Weee/Model/Tax.php
index f9c8fa904b5..c826dbe93b0 100644
--- a/app/code/Magento/Weee/Model/Tax.php
+++ b/app/code/Magento/Weee/Model/Tax.php
@@ -242,7 +242,7 @@ class Tax extends \Magento\Framework\Model\AbstractModel
         }
 
         $rateRequest = $calculator->getRateRequest($shipping, $billing, $customerTaxClass, $store);
-        $defaultRateRequest = $calculator->getRateRequest(false, false, false, $store);
+        $defaultRateRequest = $calculator->getDefaultRateRequest($store);
 
         $discountPercent = 0;
         if (!$ignoreDiscount && $this->_weeeData->isDiscounted($store)) {
diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module.less
index 09c58867ae8..1c58ab846ef 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module.less
@@ -90,6 +90,7 @@
         text-align: left;
         position: relative;
         z-index: 1;
+        margin: 0 10px;
         &.active {
             z-index: 999;
         }
@@ -122,6 +123,7 @@
         .qty.counter {
             display: inline-block;
             background: #ed4f2e;
+            color: @primary7;
             font-size: 12px;
             line-height: 12px;
             font-weight: bold;
diff --git a/app/design/adminhtml/Magento/backend/web/css/admin.less b/app/design/adminhtml/Magento/backend/web/css/admin.less
index bd89088c42b..6333aaf2fa2 100644
--- a/app/design/adminhtml/Magento/backend/web/css/admin.less
+++ b/app/design/adminhtml/Magento/backend/web/css/admin.less
@@ -3677,10 +3677,11 @@ tr.dynamic-grid input.input-text {
     float: none;
 }
 
-.fpt-item-container select {
-    width: 100%;
-
-    &:first-child {
+.data-table .fpt-item-container {
+    td {
+        vertical-align: top;
+    }
+    select:first-child {
         margin-bottom: 8px;
     }
 }
@@ -3688,6 +3689,7 @@ tr.dynamic-grid input.input-text {
 .eq-ie9 {
     .col-1-layout,
     .catalog-product-edit,
+    .catalog-product-new,
     .sales-order-view,
     .catalog-category-edit {
         table.data {
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/table.less b/app/design/adminhtml/Magento/backend/web/css/source/table.less
index 2b95c3b3630..447c0997800 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/table.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/table.less
@@ -337,7 +337,8 @@ table {
         &.col-time,
         &.col-billing_name,
         &.col-shipping_name,
-        &.col-phone {
+        &.col-phone,
+        &.col-type {
             &:extend(.ellipsis all);
         }
     }
@@ -416,6 +417,11 @@ table {
         &:extend(.col-40 all);
     }
 
+    .col-select,
+    .col-massaction {
+        text-align: center;
+    }
+
     .editable .input-text {
         width: 65px;
     }
@@ -460,7 +466,8 @@ td.col-updated_at,
 td.col-customer_since,
 td.col-session_start_time,
 td.col-time,
-td.col-sku {
+td.col-sku,
+td.col-type {
     &:extend(.nowrap all);
 }
 
@@ -1105,6 +1112,9 @@ td.col-sku {
     .rma-request-details {
         &:extend(.data-table-td-max all);
     }
+    #rma_items_grid_table .headings th {
+        &:extend(.nowrap all);
+    }
 }
 
 .adminhtml-rma-edit {
@@ -1138,6 +1148,10 @@ td.col-sku {
     }
 }
 
+.catalog-product-index .grid .hor-scroll {
+    &:extend(.h-scroll);
+}
+
 .catalog-product-review-index {
     .grid {
         .col-name,
@@ -1561,7 +1575,7 @@ td.col-sku {
 .adminhtml-cache-index,
 .adminhtml-process-list,
 .indexer-indexer-list {
-    .col-select {
+    .grid .col-select {
         width: 10px;
     }
 }
diff --git a/app/design/adminhtml/Magento/backend/web/js/theme.js b/app/design/adminhtml/Magento/backend/web/js/theme.js
index e2992b03620..01d7ba7252d 100644
--- a/app/design/adminhtml/Magento/backend/web/js/theme.js
+++ b/app/design/adminhtml/Magento/backend/web/js/theme.js
@@ -142,16 +142,22 @@
         },
 
         _leaveEffects: function (e) {
-            var targetSubmenu = $(e.target).closest('.submenu');
+            var targetSubmenu = $(e.target).closest('.submenu'),
+            self = $(this),
+            submenu = $('> .submenu', this);
+
             if(targetSubmenu.length && targetSubmenu.is(':hidden')) {
                 return;
             }
-            var self = $(this);
 
-            $('> .submenu', this)
-                .slideUp('fast', function() {
+            if(submenu.length) {
+                submenu.slideUp('fast', function() {
                     self.removeClass('hover');
                 });
+            } else {
+                self.removeClass('hover');
+            }
+
         }
     });
 
diff --git a/dev/tests/functional/lib/Mtf/Client/Driver/Selenium/Element/MultiselectlistElement.php b/dev/tests/functional/lib/Mtf/Client/Driver/Selenium/Element/MultiselectlistElement.php
index 0b2526fa6f6..8f58e9e5529 100644
--- a/dev/tests/functional/lib/Mtf/Client/Driver/Selenium/Element/MultiselectlistElement.php
+++ b/dev/tests/functional/lib/Mtf/Client/Driver/Selenium/Element/MultiselectlistElement.php
@@ -123,4 +123,22 @@ class MultiselectlistElement extends MultiselectElement
 
         return $options;
     }
+
+    /**
+     * Method that returns array with all options in multiple select list
+     *
+     * @return array
+     */
+    public function getAllValues()
+    {
+        $optionsValue = [];
+        $options = $this->getOptions();
+
+        foreach ($options as $option) {
+            /** @var Element $option */
+            $optionsValue[] = $option->getText();
+        }
+
+        return $optionsValue;
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php
index 636a537ea74..a17327a5c59 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php
@@ -29,7 +29,6 @@ use Mtf\Client\Element\Locator;
 /**
  * Class FormPageActions
  * Form page actions block
- *
  */
 class FormPageActions extends PageActions
 {
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/Price.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/Price.php
index 9360e0d761f..721a1ddb748 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/Price.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/Price.php
@@ -24,7 +24,6 @@
 namespace Magento\Catalog\Test\Block\Product;
 
 use Mtf\Block\Block;
-use Mtf\Factory\Factory;
 use Mtf\Client\Element\Locator;
 
 /**
@@ -171,7 +170,9 @@ class Price extends Block
             $priceElement = $this->_rootElement->find($this->regularPriceClass, Locator::SELECTOR_CSS);
         }
         // return the actual value of the price
-        return $priceElement->find($this->priceClass, Locator::SELECTOR_CSS)->getText();
+        $element = $priceElement->find($this->priceClass, Locator::SELECTOR_CSS);
+        $price = preg_replace('#[^\d\.\s]+#umis', '', $element->getText());
+        return number_format(trim($price), 2);
     }
 
     /**
@@ -181,13 +182,10 @@ class Price extends Block
      */
     public function getSpecialPrice()
     {
-        return $this->_rootElement->find(
-            $this->specialPriceClass,
-            Locator::SELECTOR_CSS
-        )->find(
-            $this->priceClass,
-            Locator::SELECTOR_CSS
-        )->getText();
+        $element = $this->_rootElement->find($this->specialPriceClass, Locator::SELECTOR_CSS)
+            ->find($this->priceClass, Locator::SELECTOR_CSS);
+        $price = preg_replace('#[^\d\.\s]+#umis', '', $element->getText());
+        return number_format(trim($price), 2);
     }
 
     /**
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCart.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCart.php
index e72639796d6..83987d7abd5 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCart.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCart.php
@@ -114,7 +114,7 @@ class AssertProductInCart extends AbstractConstraint
 
         $price = $checkoutCart->getCartBlock()->getProductPriceByName($productName);
         \PHPUnit_Framework_Assert::assertEquals(
-            '$' . number_format($priceComparing, 2),
+            number_format($priceComparing, 2),
             $price,
             'Product price in shopping cart is not correct.'
         );
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCategory.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCategory.php
index 58c7fd8faed..164eafe6024 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCategory.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCategory.php
@@ -87,9 +87,8 @@ class AssertProductInCategory extends AbstractConstraint
         $price = $catalogCategoryView->getListProductBlock()->getProductPriceBlock($product->getName())
             ->getRegularPrice();
 
-        $priceComparing = '$' . number_format($product->getPrice(), 2);
         \PHPUnit_Framework_Assert::assertEquals(
-            $priceComparing,
+            number_format($product->getPrice(), 2),
             $price,
             'Product regular price on category page is not correct.'
         );
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple/Price.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple/Price.php
index a8726289546..19d881b7ad2 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple/Price.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple/Price.php
@@ -107,35 +107,35 @@ class Price implements FixtureInterface
     {
         $presets = [
             'MAGETWO-23062' => [
-                'category_price' => '$100.00',
-                'product_price' => '$100.00',
-                'cart_price' => '$130.00'
+                'category_price' => '100.00',
+                'product_price' => '100.00',
+                'cart_price' => '130.00'
             ],
             'MAGETWO-23063' => [
-                'category_price' => '$100.00',
-                'product_price' => '$100.00',
-                'cart_price' => '$140.00'
+                'category_price' => '100.00',
+                'product_price' => '100.00',
+                'cart_price' => '140.00'
             ],
             'MAGETWO-23029' => [
-                'category_price' => '$100.00',
-                'category_special_price' => '$90.00',
-                'product_price' => '$100.00',
-                'product_special_price' => '$90.00',
-                'cart_price' => '$120.00'
+                'category_price' => '100.00',
+                'category_special_price' => '90.00',
+                'product_price' => '100.00',
+                'product_special_price' => '90.00',
+                'cart_price' => '120.00'
             ],
             'MAGETWO-23030' => [
-                'category_price' => '$100.00',
-                'category_special_price' => '$90.00',
-                'product_price' => '$100.00',
-                'product_special_price' => '$90.00',
-                'cart_price' => '$126.00'
+                'category_price' => '100.00',
+                'category_special_price' => '90.00',
+                'product_price' => '100.00',
+                'product_special_price' => '90.00',
+                'cart_price' => '126.00'
             ],
             'MAGETWO-23036' => [
-                'category_price' => '$100.00',
-                'category_special_price' => '$90.00',
-                'product_price' => '$100.00',
-                'product_special_price' => '$90.00',
-                'cart_price' => '$90.00'
+                'category_price' => '100.00',
+                'category_special_price' => '90.00',
+                'product_price' => '100.00',
+                'product_special_price' => '90.00',
+                'cart_price' => '90.00'
             ]
         ];
         if (!isset($presets[$this->currentPreset])) {
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php
index 8b5a5130316..79e02713099 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php
@@ -73,6 +73,27 @@ class Cart extends Block
      */
     protected $cartProductPrice = '//tr[string(td/div/strong/a)="%s"]/td[@class="col price excl tax"]/span/span';
 
+    /**
+     * 'Update Shopping Cart' button
+     *
+     * @var string
+     */
+    protected $updateShoppingCart = '[name="update_cart_action"]';
+
+    /**
+     * Quantity input selector
+     *
+     * @var string
+     */
+    protected $productQty = '//input[@type="number" and @title="Qty"]';
+
+    /**
+     * Cart item selector
+     *
+     * @var string
+     */
+    protected $cartItem = '//tr[normalize-space(td)="%s"]';
+
     /**
      * Get sub-total for the specified item in the cart
      *
@@ -81,9 +102,7 @@ class Cart extends Block
      */
     public function getCartItemSubTotal($product)
     {
-        $selector = '//tr[normalize-space(td)="' . $this->getProductName(
-            $product
-        ) . '"]' . $this->itemSubTotalSelector;
+        $selector = sprintf($this->cartItem, $this->getProductName($product)) . $this->itemSubTotalSelector;
         return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText();
     }
 
@@ -95,8 +114,9 @@ class Cart extends Block
      */
     public function getCartItemSubTotalByProductName($productName)
     {
-        $selector = '//tr[normalize-space(td)="' . $productName . '"]' . $this->itemSubTotalSelector;
-        return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText();
+        $selector = sprintf($this->cartItem, $productName) . $this->itemSubTotalSelector;
+        $itemSubtotal = $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText();
+        return $this->escapeCurrency($itemSubtotal);
     }
 
     /**
@@ -108,15 +128,9 @@ class Cart extends Block
      */
     public function getCartItemUnitPrice($product, $currency = '$')
     {
-        $selector = '//tr[normalize-space(td)="' . $this->getProductName(
-            $product
-        ) . '"]' . $this->itemUnitPriceSelector;
-
+        $selector = sprintf($this->cartItem, $this->getProductName($product)) . $this->itemUnitPriceSelector;
         $prices = explode("\n", trim($this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText()));
-        if (count($prices) == 1) {
-            return floatval(trim($prices[0], $currency));
-        }
-        return $this->formatPricesData($prices, $currency);
+        return floatval(trim($prices[0], $currency));
     }
 
     /**
@@ -236,7 +250,7 @@ class Cart extends Block
     public function isProductInShoppingCart($product)
     {
         return $this->_rootElement->find(
-            '//tr[normalize-space(td)="' . $this->getProductName($product) . '"]',
+            sprintf($this->cartItem, $this->getProductName($product)),
             Locator::SELECTOR_XPATH
         )->isVisible();
     }
@@ -268,6 +282,54 @@ class Cart extends Block
     public function getProductPriceByName($productName)
     {
         $priceSelector = sprintf($this->cartProductPrice, $productName);
-        return $this->_rootElement->find($priceSelector, Locator::SELECTOR_XPATH)->getText();
+        $cartProductPrice = $this->_rootElement->find($priceSelector, Locator::SELECTOR_XPATH)->getText();
+        return $this->escapeCurrency($cartProductPrice);
+    }
+
+    /**
+     * Update shopping cart
+     *
+     * @return void
+     */
+    public function updateShoppingCart()
+    {
+        $this->_rootElement->find($this->updateShoppingCart, Locator::SELECTOR_CSS)->click();
+    }
+
+    /**
+     * Set product quantity
+     *
+     * @param string $productName
+     * @param int $qty
+     * @return void
+     */
+    public function setProductQty($productName, $qty)
+    {
+        $productQtySelector = sprintf($this->cartItem, $productName) . $this->productQty;
+        $this->_rootElement->find($productQtySelector, Locator::SELECTOR_XPATH)->setValue($qty);
+    }
+
+    /**
+     * Get product quantity
+     *
+     * @param string $productName
+     * @return string
+     */
+    public function getProductQty($productName)
+    {
+        $productQtySelector = sprintf($this->cartItem, $productName) . $this->productQty;
+        return $this->_rootElement->find($productQtySelector, Locator::SELECTOR_XPATH)->getValue();
+    }
+
+    /**
+     * Method that escapes currency symbols
+     *
+     * @param string $price
+     * @return string
+     */
+    protected function escapeCurrency($price)
+    {
+        preg_match("/^\\D*\\s*([\\d,\\.]+)\\s*\\D*$/", $price, $matches);
+        return (isset($matches[1])) ? $matches[1] : null;
     }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar.php
new file mode 100644
index 00000000000..c557d720fd3
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Checkout\Test\Block\Cart;
+
+use Mtf\Block\Block;
+use Mtf\Client\Element\Locator;
+
+/**
+ * Class Sidebar
+ * Mini shopping cart block
+ */
+class Sidebar extends Block
+{
+    /**
+     * Quantity input selector
+     *
+     * @var string
+     */
+    protected $qty = '//*[@class="product"]/*[@title="%s"]/following-sibling::*//*[@class="value qty"]';
+
+    /**
+     * Mini cart link selector
+     *
+     * @var string
+     */
+    protected $cartLink = 'a.showcart';
+
+    /**
+     * Mini cart content selector
+     *
+     * @var string
+     */
+    protected $cartContent = 'div.minicart';
+
+    /**
+     * Open mini cart
+     *
+     * @return void
+     */
+    public function openMiniCart()
+    {
+        if (!$this->_rootElement->find($this->cartContent)->isVisible()) {
+            $this->_rootElement->find($this->cartLink)->click();
+        }
+    }
+
+    /**
+     * Get product quantity
+     *
+     * @param string $productName
+     * @return string
+     */
+    public function getProductQty($productName)
+    {
+        $this->openMiniCart();
+        $productQty = sprintf($this->qty, $productName);
+        return $this->_rootElement->find($productQty, Locator::SELECTOR_XPATH)->getText();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php
new file mode 100644
index 00000000000..96949593e8a
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Checkout\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Checkout\Test\Page\CheckoutCart;
+use Magento\Checkout\Test\Fixture\Cart;
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+
+/**
+ * Class AssertPriceInShoppingCart
+ */
+class AssertPriceInShoppingCart extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'low';
+
+    /**
+     * Assert that price in the shopping cart equals to expected price from data set
+     *
+     * @param CheckoutCart $checkoutCart
+     * @param Cart $cart
+     * @param CatalogProductSimple $product
+     * @return void
+     */
+    public function processAssert(
+        CheckoutCart $checkoutCart,
+        Cart $cart,
+        CatalogProductSimple $product
+    ) {
+        $cartProductPrice = $checkoutCart->open()->getCartBlock()->getProductPriceByName($product->getName());
+        \PHPUnit_Framework_Assert::assertEquals(
+            $cartProductPrice,
+            $cart->getPrice(),
+            'Shopping cart product price: \'' . $cartProductPrice
+            . '\' not equals with price from data set: \'' . $cart->getPrice() . '\''
+        );
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Price in the shopping cart equals to expected price from data set.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInMiniShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInMiniShoppingCart.php
new file mode 100644
index 00000000000..d13ad060413
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInMiniShoppingCart.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Checkout\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Cms\Test\Page\CmsIndex;
+use Magento\Checkout\Test\Fixture\Cart;
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+
+/**
+ * Class AssertProductQtyInMiniShoppingCart
+ */
+class AssertProductQtyInMiniShoppingCart extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'low';
+
+    /**
+     * Assert that product quantity in the mini shopping cart is equals to expected quantity from data set
+     *
+     * @param CmsIndex $cmsIndex
+     * @param Cart $cart
+     * @param CatalogProductSimple $product
+     * @return void
+     */
+    public function processAssert(
+        CmsIndex $cmsIndex,
+        Cart $cart,
+        CatalogProductSimple $product
+    ) {
+        $productQtyInMiniCart = $cmsIndex->open()->getCartSidebarBlock()->getProductQty($product->getName());
+        \PHPUnit_Framework_Assert::assertEquals(
+            $productQtyInMiniCart,
+            $cart->getQty(),
+            'Mini shopping cart product qty: \'' . $productQtyInMiniCart
+            . '\' not equals with qty from data set: \'' . $cart->getQty() . '\''
+        );
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Quantity in the mini shopping cart equals to expected quantity from data set.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php
new file mode 100644
index 00000000000..fa27cfce7b7
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Checkout\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Checkout\Test\Page\CheckoutCart;
+use Magento\Checkout\Test\Fixture\Cart;
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+
+/**
+ * Class AssertProductQtyInShoppingCart
+ */
+class AssertProductQtyInShoppingCart extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'low';
+
+    /**
+     * Assert that quantity in the shopping cart is equals to expected quantity from data set
+     *
+     * @param CheckoutCart $checkoutCart
+     * @param Cart $cart
+     * @param CatalogProductSimple $product
+     * @return void
+     */
+    public function processAssert(
+        CheckoutCart $checkoutCart,
+        Cart $cart,
+        CatalogProductSimple $product
+    ) {
+        $cartProductQty = $checkoutCart->open()->getCartBlock()->getProductQty($product->getName());
+        \PHPUnit_Framework_Assert::assertEquals(
+            $cartProductQty,
+            $cart->getQty(),
+            'Shopping cart product qty: \'' . $cartProductQty
+            . '\' not equals with qty from data set: \'' . $cart->getQty() . '\''
+        );
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Quantity in the shopping cart equals to expected quantity from data set.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php
new file mode 100644
index 00000000000..c788bc26dcb
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Checkout\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Checkout\Test\Page\CheckoutCart;
+use Magento\Checkout\Test\Fixture\Cart;
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+
+/**
+ * Class AssertSubtotalInShoppingCart
+ */
+class AssertSubtotalInShoppingCart extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'low';
+
+    /**
+     * Assert that subtotal total in the shopping cart is equals to expected total from data set
+     *
+     * @param CheckoutCart $checkoutCart
+     * @param Cart $cart
+     * @param CatalogProductSimple $product
+     * @return void
+     */
+    public function processAssert(
+        CheckoutCart $checkoutCart,
+        Cart $cart,
+        CatalogProductSimple $product
+    ) {
+        $cartProductSubtotal = $checkoutCart->open()->getCartBlock()
+            ->getCartItemSubTotalByProductName($product->getName());
+        \PHPUnit_Framework_Assert::assertEquals(
+            $cartProductSubtotal,
+            $cart->getRowTotal(),
+            'Shopping cart subtotal: \'' . $cartProductSubtotal
+            . '\' not equals with total from data set: \'' . $cart->getRowTotal() . '\''
+        );
+    }
+
+    /**
+     * Returns a string representation of the object.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Subtotal in the shopping cart equals to expected total from data set.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/constraint.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/constraint.xml
new file mode 100644
index 00000000000..041a6ac5bf0
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/constraint.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" ?>
+<!--
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Academic Free License (AFL 3.0)
+ * that is bundled with this package in the file LICENSE_AFL.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/afl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
+ */
+-->
+<constraint>
+    <assertPriceInShoppingCart module="Magento_Checkout">
+        <severeness>low</severeness>
+    </assertPriceInShoppingCart>
+    <assertProductQtyInShoppingCart module="Magento_Checkout">
+        <severeness>low</severeness>
+    </assertProductQtyInShoppingCart>
+    <assertProductQtyInMiniShoppingCart module="Magento_Checkout">
+        <severeness>low</severeness>
+    </assertProductQtyInMiniShoppingCart>
+    <assertSubtotalInShoppingCart module="Magento_Checkout">
+        <severeness>low</severeness>
+    </assertSubtotalInShoppingCart>
+</constraint>
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/fixture.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/fixture.xml
new file mode 100644
index 00000000000..e9d8be3cb26
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/etc/global/fixture.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" ?>
+<!--
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Academic Free License (AFL 3.0)
+ * that is bundled with this package in the file LICENSE_AFL.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/afl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
+ */
+-->
+<fixture>
+    <cart module="Magento_Checkout">
+        <type>flat</type>
+        <entity_type>sales_flat_quote_item</entity_type>
+        <collection>Magento\Checkout\Model\Resource\Cart</collection>
+    </cart>
+</fixture>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Actions.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/CmsPageInterface.php
similarity index 70%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Actions.php
rename to dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/CmsPageInterface.php
index 01ae4c2757b..f111082b687 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Actions.php
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/CmsPageInterface.php
@@ -1,7 +1,5 @@
 <?php
 /**
- * URL rewrite grid actions
- *
  * Magento
  *
  * NOTICE OF LICENSE
@@ -23,24 +21,15 @@
  * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
-namespace Magento\UrlRewrite\Test\Block;
 
-use Mtf\Block\Block;
+namespace Magento\Cms\Test\Handler\CmsPage;
 
-class Actions extends Block
-{
-    /**
-     * Add button
-     *
-     * @var string
-     */
-    protected $addNewButton = '#add';
+use Mtf\Handler\HandlerInterface;
 
-    /**
-     * Add new URL rewrite
-     */
-    public function addNewUrlRewrite()
-    {
-        $this->_rootElement->find($this->addNewButton)->click();
-    }
+/**
+ * Interface CmsPageInterface
+ */
+interface CmsPageInterface extends HandlerInterface
+{
+   //
 }
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/Curl.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/Curl.php
new file mode 100644
index 00000000000..a9cd22332ce
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Handler/CmsPage/Curl.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Cms\Test\Handler\CmsPage;
+
+use Mtf\Fixture\FixtureInterface;
+use Mtf\Handler\Curl as AbstractCurl;
+use Mtf\Util\Protocol\CurlInterface;
+use Mtf\Util\Protocol\CurlTransport;
+use Mtf\Util\Protocol\CurlTransport\BackendDecorator;
+use Mtf\System\Config;
+
+/**
+ * Class Curl
+ * Curl handler for creating cms page
+ */
+class Curl extends AbstractCurl implements CmsPageInterface
+{
+    /**
+     * Data mapping
+     *
+     * @var array
+     */
+    protected $mappingData = [
+        'status' => ['Published' => 1, 'Disabled' => 0],
+        'store_id' => ['All Store Views' => 0],
+    ];
+
+    /**
+     * Url for save rewrite
+     *
+     * @var string
+     */
+    protected $url = 'admin/cms_page/save/back/edit/active_tab/content_section/';
+
+    /**
+     * Post request for creating cms page
+     *
+     * @param FixtureInterface $fixture
+     * @return array
+     * @throws \Exception
+     */
+    public function persist(FixtureInterface $fixture = null)
+    {
+        $url = $_ENV['app_backend_url'] . $this->url;
+        $data = $this->replaceMappingData($fixture->getData());
+        $curl = new BackendDecorator(new CurlTransport(), new Config());
+        $curl->write(CurlInterface::POST, $url, '1.0', [], $data);
+        $response = $curl->read();
+
+        if (!strpos($response, 'data-ui-id="messages-message-success"')) {
+            throw new \Exception("Page creation by curl handler was not successful! Response: $response");
+        }
+
+        preg_match("~page_id\/(\d*?)\/~", $response, $matches);
+        $id = isset($matches[1]) ? $matches[1] : null;
+        return ['page_id' => $id];
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.php
index 6c5e2cf5df7..2e110104a54 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.php
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.php
@@ -70,6 +70,12 @@ class CmsIndex extends FrontendPage
             'locator' => '[data-ui-id="language-switcher"]',
             'strategy' => 'css selector',
         ],
+        'cartSidebarBlock' => [
+            'name' => 'cartSidebarBlock',
+            'class' => 'Magento\Checkout\Test\Block\Cart\Sidebar',
+            'locator' => '[data-block="minicart"]',
+            'strategy' => 'css selector',
+        ],
     ];
 
     /**
@@ -119,4 +125,12 @@ class CmsIndex extends FrontendPage
     {
         return $this->getBlockInstance('storeSwitcherBlock');
     }
+
+    /**
+     * @return \Magento\Checkout\Test\Block\Cart\Sidebar
+     */
+    public function getCartSidebarBlock()
+    {
+        return $this->getBlockInstance('cartSidebarBlock');
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml
index a28d9698d88..0f03a95f956 100644
--- a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml
+++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml
@@ -60,4 +60,10 @@
         <locator>//*[@data-ui-id="language-switcher"]</locator>
         <strategy>css selector</strategy>
     </block>
+    <block>
+        <name>cartSidebarBlock</name>
+        <class>Magento\Checkout\Test\Block\Cart\Sidebar</class>
+        <locator>[data-block="minicart"]</locator>
+        <strategy>css selector</strategy>
+    </block>
 </page>
diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableInCategory.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableInCategory.php
index 03c60d74f23..19b546ec68a 100644
--- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableInCategory.php
+++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableInCategory.php
@@ -105,7 +105,7 @@ class AssertConfigurableInCategory extends AbstractConstraint
                 ->getProductPriceBlock($configurable->getName())
                 ->getPrice();
             \PHPUnit_Framework_Assert::assertEquals(
-                '$' . $price['price_regular_price'],
+                $price['price_regular_price'],
                 $pricePresetData['category_price'],
                 'Product price on category page is not correct.'
             );
diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableView.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableView.php
index 6a047a0f2ea..2bb1333dd39 100644
--- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableView.php
+++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableView.php
@@ -94,7 +94,7 @@ class AssertConfigurableView extends AbstractConstraint
                 ->getProductPriceBlock($configurable->getName())
                 ->getPrice();
             \PHPUnit_Framework_Assert::assertEquals(
-                '$' . $price['price_regular_price'],
+                $price['price_regular_price'],
                 $pricePresetData['product_price'],
                 'Product price on category page is not correct.'
             );
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingInGrid.php b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingInGrid.php
index c21132f3c50..d6307d1e643 100644
--- a/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingInGrid.php
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingInGrid.php
@@ -24,6 +24,8 @@
 
 namespace Magento\Review\Test\Constraint;
 
+use Magento\Review\Test\Fixture\Rating;
+use Magento\Review\Test\Page\Adminhtml\RatingIndex;
 use Mtf\Constraint\AbstractConstraint;
 
 /**
@@ -39,18 +41,30 @@ class AssertProductRatingInGrid extends AbstractConstraint
     protected $severeness = 'middle';
 
     /**
+     * Assert product Rating availability in product Rating grid
+     *
+     * @param RatingIndex $ratingIndex
+     * @param Rating $productRating
      * @return void
      */
-    public function processAssert()
+    public function processAssert(RatingIndex $ratingIndex, Rating $productRating)
     {
-        //
+        $filter = ['rating_code' => $productRating->getRatingCode()];
+
+        $ratingIndex->open();
+        \PHPUnit_Framework_Assert::assertTrue(
+            $ratingIndex->getRatingGrid()->isRowVisible($filter),
+            "Product Rating " . $productRating->getRatingCode() . " is absent on product Rating grid."
+        );
     }
 
     /**
+     * Text success exist product Rating in grid
+     *
      * @return string
      */
     public function toString()
     {
-        //
+        return 'Product Rating is present in grid.';
     }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingNotInGrid.php b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingNotInGrid.php
new file mode 100644
index 00000000000..41c760abbdf
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingNotInGrid.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Review\Test\Constraint;
+
+use Magento\Review\Test\Fixture\Rating;
+use Magento\Review\Test\Page\Adminhtml\RatingIndex;
+use Mtf\Constraint\AbstractConstraint;
+
+/**
+ * Class AssertProductRatingNotInGrid
+ */
+class AssertProductRatingNotInGrid extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'middle';
+
+    /**
+     * Assert product Rating is absent on product Rating grid
+     *
+     * @param RatingIndex $ratingIndex
+     * @param Rating $productRating
+     * @return void
+     */
+    public function processAssert(RatingIndex $ratingIndex, Rating $productRating)
+    {
+        $filter = ['rating_code' => $productRating->getRatingCode()];
+
+        $ratingIndex->open();
+        \PHPUnit_Framework_Assert::assertFalse(
+            $ratingIndex->getRatingGrid()->isRowVisible($filter),
+            "Product Rating " . $productRating->getRatingCode() . " is exist on product Rating grid."
+        );
+    }
+
+    /**
+     * Text success absent product Rating in grid
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Product Rating is absent in grid.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingSuccessDeleteMessage.php b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingSuccessDeleteMessage.php
new file mode 100644
index 00000000000..33e9dbcaf4c
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Constraint/AssertProductRatingSuccessDeleteMessage.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Review\Test\Constraint;
+
+use Magento\Review\Test\Page\Adminhtml\RatingIndex;
+use Mtf\Constraint\AbstractConstraint;
+
+/**
+ * Class AssertProductRatingSuccessDeleteMessage
+ */
+class AssertProductRatingSuccessDeleteMessage extends AbstractConstraint
+{
+    const SUCCESS_DELETE_MESSAGE = 'You deleted the rating.';
+
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'high';
+
+    /**
+     * Assert that success message is displayed after rating delete
+     *
+     * @param RatingIndex $ratingIndex
+     * @return void
+     */
+    public function processAssert(RatingIndex $ratingIndex)
+    {
+        $actualMessage = $ratingIndex->getMessagesBlock()->getSuccessMessages();
+        \PHPUnit_Framework_Assert::assertEquals(
+            self::SUCCESS_DELETE_MESSAGE,
+            $actualMessage,
+            'Wrong success message is displayed.'
+            . "\nExpected: " . self::SUCCESS_DELETE_MESSAGE
+            . "\nActual: " . $actualMessage
+        );
+    }
+
+    /**
+     * Text success delete message is displayed
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Rating success delete message is present.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Fixture/Rating.php b/dev/tests/functional/tests/app/Magento/Review/Test/Fixture/Rating.php
index 517aa62be43..447987b294d 100644
--- a/dev/tests/functional/tests/app/Magento/Review/Test/Fixture/Rating.php
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Fixture/Rating.php
@@ -34,12 +34,12 @@ class Rating extends InjectableFixture
     /**
      * @var string
      */
-    protected $repositoryClass = 'Magento\Rating\Test\Repository\Rating';
+    protected $repositoryClass = 'Magento\Review\Test\Repository\Rating';
 
     /**
      * @var string
      */
-    protected $handlerInterface = 'Magento\Rating\Test\Handler\Rating\RatingInterface';
+    protected $handlerInterface = 'Magento\Review\Test\Handler\Rating\RatingInterface';
 
     protected $defaultDataSet = [
         'rating_code' => 'Rating %isolation%',
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/Curl.php b/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/Curl.php
new file mode 100644
index 00000000000..2672dc9b45a
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/Curl.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Review\Test\Handler\Rating;
+
+use Magento\Backend\Test\Handler\Extractor;
+use Mtf\Fixture\FixtureInterface;
+use Mtf\Handler\Curl as AbstractCurl;
+use Mtf\System\Config;
+use Mtf\Util\Protocol\CurlInterface;
+use Mtf\Util\Protocol\CurlTransport\BackendDecorator;
+use Mtf\Util\Protocol\CurlTransport;
+
+/**
+ * Class Curl
+ * Curl handler for creating product Rating through backend.
+ */
+class Curl extends AbstractCurl implements RatingInterface
+{
+    /**
+     * Mapping values for data.
+     *
+     * @var array
+     */
+    protected $mappingData = [
+        'is_active' => [
+            'Yes' => 1,
+            'No' => 0,
+        ],
+        'stores' => [
+            'Main Website/Main Website Store/Default Store View' => 1
+        ]
+    ];
+
+    /**
+     * Post request for creating product Rating in backend
+     *
+     * @param FixtureInterface|null $rating
+     * @return array
+     * @throws \Exception
+     */
+    public function persist(FixtureInterface $rating = null)
+    {
+        $url = $_ENV['app_backend_url'] . 'review/rating/save/';
+        $curl = new BackendDecorator(new CurlTransport(), new Config());
+        $data = $this->replaceMappingData($rating->getData());
+
+        $data['stores'] = is_array($data['stores']) ? $data['stores'] : [$data['stores']];
+        $curl->write(CurlInterface::POST, $url, '1.0', [], $data);
+        $response = $curl->read();
+        $curl->close();
+        if (!strpos($response, 'data-ui-id="messages-message-success"')) {
+            throw new \Exception(
+                'Product Rating entity creating by curl handler was not successful! Response:' . $response
+            );
+        }
+
+        return ['id' => $this->getProductRatingId()];
+    }
+
+    /**
+     * Get product Rating id
+     *
+     * @return int|null
+     */
+    protected function getProductRatingId()
+    {
+        $url = 'review/rating/index/sort/rating_id/dir/desc/';
+        $regex = '/data-column="rating_id"[^>]*>\s*([0-9]+)\s*</';
+        $extractor = new Extractor($url, $regex);
+        $match = $extractor->getData();
+
+        return empty($match[1]) ? null : $match[1];
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/RatingInterface.php b/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/RatingInterface.php
new file mode 100644
index 00000000000..f3bea35c9ae
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Handler/Rating/RatingInterface.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Review\Test\Handler\Rating;
+
+use Mtf\Handler\HandlerInterface;
+
+/**
+ * Interface RatingInterface
+ */
+interface RatingInterface extends HandlerInterface
+{
+    //
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Repository/Rating.php b/dev/tests/functional/tests/app/Magento/Review/Test/Repository/Rating.php
new file mode 100644
index 00000000000..4141b11447d
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Repository/Rating.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Review\Test\Repository;
+
+use Mtf\Repository\AbstractRepository;
+
+/**
+ * Class Rating
+ * Data for creation product Rating
+ */
+class Rating extends AbstractRepository
+{
+    /**
+     * @constructor
+     * @param array $defaultConfig
+     * @param array $defaultData
+     */
+    public function __construct(array $defaultConfig = [], array $defaultData = [])
+    {
+        $this->_data['default'] = [
+            'rating_code' => 'Rating %isolation%',
+            'stores' => ['Main Website/Main Website Store/Default Store View'],
+            'is_active' => 'Yes',
+        ];
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateBackendProductRatingTest.php b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateProductRatingEntityTest.php
similarity index 96%
rename from dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateBackendProductRatingTest.php
rename to dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateProductRatingEntityTest.php
index e940b149769..5d6f3dda381 100644
--- a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateBackendProductRatingTest.php
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateProductRatingEntityTest.php
@@ -50,7 +50,7 @@ use Mtf\TestCase\Injectable;
  * @group Reviews_and_Ratings_(MX)
  * @ZephyrId MAGETWO-23331
  */
-class CreateBackendProductRatingTest extends Injectable
+class CreateProductRatingEntityTest extends Injectable
 {
     /**
      * @var Rating
@@ -97,7 +97,7 @@ class CreateBackendProductRatingTest extends Injectable
      * @param Rating $productRating
      * @return void
      */
-    public function testCreateBackendProductRating(
+    public function testCreateProductRatingEntityTest(
         CatalogProductSimple $product,
         Rating $productRating
     ) {
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateBackendProductRatingTest/testCreateBackendProductRating.csv b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateProductRatingEntityTest/testCreateProductRatingEntityTest.csv
similarity index 100%
rename from dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateBackendProductRatingTest/testCreateBackendProductRating.csv
rename to dev/tests/functional/tests/app/Magento/Review/Test/TestCase/CreateProductRatingEntityTest/testCreateProductRatingEntityTest.csv
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest.php b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest.php
new file mode 100644
index 00000000000..a9d58fc44b1
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Review\Test\TestCase;
+
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+use Magento\Review\Test\Fixture\Rating;
+use Magento\Review\Test\Page\Adminhtml\RatingEdit;
+use Magento\Review\Test\Page\Adminhtml\RatingIndex;
+use Mtf\TestCase\Injectable;
+
+/**
+ * Test Creation for DeleteProductRatingEntity
+ *
+ * Test Flow:
+ *
+ * Preconditions:
+ * 1. Simple product is created.
+ * 2. Product rating is created.
+ *
+ * Steps:
+ * 1. Login to backend.
+ * 2. Navigate to Stores->Attributes->Rating.
+ * 3. Search product rating in grid by given data.
+ * 4. Open this product rating by clicking.
+ * 5. Click 'Delete Rating' button.
+ * 6. Perform all asserts.
+ *
+ * @group Reviews_and_Ratings_(MX)
+ * @ZephyrId MAGETWO-23276
+ */
+class DeleteProductRatingEntityTest extends Injectable
+{
+    /**
+     * @var RatingIndex
+     */
+    protected $ratingIndex;
+
+    /**
+     * @var RatingEdit
+     */
+    protected $ratingEdit;
+
+    /**
+     * Prepare data
+     *
+     * @param CatalogProductSimple $product
+     * @return array
+     */
+    public function __prepare(CatalogProductSimple $product)
+    {
+        $product->persist();
+        return ['product' => $product];
+    }
+
+    /**
+     * Inject data
+     *
+     * @param RatingIndex $ratingIndex
+     * @param RatingEdit $ratingEdit
+     * @return void
+     */
+    public function __inject(RatingIndex $ratingIndex, RatingEdit $ratingEdit)
+    {
+        $this->ratingIndex = $ratingIndex;
+        $this->ratingEdit = $ratingEdit;
+    }
+
+    /**
+     * Runs delete product Rating entity test
+     *
+     * @param Rating $productRating
+     * @return void
+     */
+    public function testDeleteProductRatingEntity(Rating $productRating)
+    {
+        // Preconditions
+        $productRating->persist();
+
+        // Steps
+        $this->ratingIndex->open();
+        $this->ratingIndex->getRatingGrid()->searchAndOpen(['rating_code' => $productRating->getRatingCode()]);
+        $this->ratingEdit->getPageActions()->delete();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest/testDeleteProductRatingEntity.csv b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest/testDeleteProductRatingEntity.csv
new file mode 100644
index 00000000000..ef9e67e693c
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/DeleteProductRatingEntityTest/testDeleteProductRatingEntity.csv
@@ -0,0 +1,2 @@
+"productRating/dataSet";"constraint"
+"default";"assertProductRatingSuccessDeleteMessage, assertProductRatingNotInGrid, assertProductRatingNotInProductPage"
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/etc/curl/di.xml b/dev/tests/functional/tests/app/Magento/Review/Test/etc/curl/di.xml
new file mode 100644
index 00000000000..fa49f6b3789
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/etc/curl/di.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" ?>
+<!--
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Academic Free License (AFL 3.0)
+ * that is bundled with this package in the file LICENSE_AFL.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/afl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
+    <preference for="Magento\Review\Test\Handler\Rating\RatingInterface" type="\Magento\Review\Test\Handler\Rating\Curl"/>
+</config>
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/etc/global/constraint.xml b/dev/tests/functional/tests/app/Magento/Review/Test/etc/global/constraint.xml
index d69c444ee7c..d6f63c99b49 100644
--- a/dev/tests/functional/tests/app/Magento/Review/Test/etc/global/constraint.xml
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/etc/global/constraint.xml
@@ -27,9 +27,15 @@
     <assertProductRatingSuccessSaveMessage module="Magento_Review">
         <severeness>high</severeness>
     </assertProductRatingSuccessSaveMessage>
+    <assertProductRatingSuccessDeleteMessage module="Magento_Review">
+        <severeness>high</severeness>
+    </assertProductRatingSuccessDeleteMessage>
     <assertProductRatingInGrid module="Magento_Review">
         <severeness>middle</severeness>
     </assertProductRatingInGrid>
+    <assertProductRatingNotInGrid module="Magento_Review">
+        <severeness>middle</severeness>
+    </assertProductRatingNotInGrid>
     <assertProductRatingInProductPage module="Magento_Review">
         <severeness>middle</severeness>
     </assertProductRatingInProductPage>
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rate/Edit/FormPageActions.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rate/Edit/FormPageActions.php
index 659a93a506a..5407bf6cf5f 100644
--- a/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rate/Edit/FormPageActions.php
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rate/Edit/FormPageActions.php
@@ -38,4 +38,11 @@ class FormPageActions extends ParentFormPageActions
      * @var string
      */
     protected $saveButton = '.save-rate';
+
+    /**
+     * "Delete" button
+     *
+     * @var string
+     */
+    protected $deleteButton = '.delete';
 }
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rule/Edit/Form.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rule/Edit/Form.php
index fcf51672418..ed71e7fa850 100644
--- a/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rule/Edit/Form.php
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Block/Adminhtml/Rule/Edit/Form.php
@@ -238,4 +238,16 @@ class Form extends FormInterface
     {
         $this->_rootElement->find($this->additionalSettings)->click();
     }
+
+    /**
+     * Getting all options in Tax Rate multi select list
+     *
+     * @return array
+     */
+    public function getAllTaxRates()
+    {
+        /** @var \Mtf\Client\Driver\Selenium\Element\MultiselectlistElement $taxRates */
+        $taxRates = $this->_rootElement->find($this->taxRateBlock, Locator::SELECTOR_CSS, 'multiselectlist');
+        return $taxRates->getAllValues();
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInGrid.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInGrid.php
new file mode 100644
index 00000000000..b327d77bc03
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInGrid.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Tax\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Tax\Test\Page\Adminhtml\TaxRateIndex;
+use Magento\Tax\Test\Fixture\TaxRate;
+
+/**
+ * Class AssertTaxRateNotInGrid
+ */
+class AssertTaxRateNotInGrid extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'high';
+
+    /**
+     * Assert that tax rate not available in Tax Rate grid
+     *
+     * @param TaxRateIndex $taxRateIndex
+     * @param TaxRate $taxRate
+     * @return void
+     */
+    public function processAssert(
+        TaxRateIndex $taxRateIndex,
+        TaxRate $taxRate
+    ) {
+        $filter = [
+            'code' => $taxRate->getCode(),
+        ];
+
+        $taxRateIndex->open();
+        \PHPUnit_Framework_Assert::assertFalse(
+            $taxRateIndex->getTaxRateGrid()->isRowVisible($filter),
+            'Tax Rate \'' . $filter['code'] . '\' is present in Tax Rate grid.'
+        );
+    }
+
+    /**
+     * Text of Tax Rate not in grid assert
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Tax rate is absent in grid.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInTaxRule.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInTaxRule.php
new file mode 100644
index 00000000000..8a8c615c8f5
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateNotInTaxRule.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Tax\Test\Constraint;
+
+use Magento\Tax\Test\Fixture\TaxRate;
+use Magento\Tax\Test\Page\Adminhtml\TaxRuleNew;
+use Mtf\Constraint\AbstractConstraint;
+
+/**
+ * Class AssertTaxRateNotInTaxRule
+ */
+class AssertTaxRateNotInTaxRule extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'high';
+
+    /**
+     * Assert that tax rate is absent in tax rule form
+     *
+     * @param TaxRate $taxRate
+     * @param TaxRuleNew $taxRuleNew
+     * @return void
+     */
+    public function processAssert(
+        TaxRate $taxRate,
+        TaxRuleNew $taxRuleNew
+    ) {
+        $taxRuleNew->open();
+        $taxRatesList = $taxRuleNew->getTaxRuleForm()->getAllTaxRates();
+        \PHPUnit_Framework_Assert::assertFalse(
+            in_array($taxRate->getCode(), $taxRatesList),
+            'Tax Rate \'' . $taxRate->getCode() . '\' is present in Tax Rule form.'
+        );
+    }
+
+    /**
+     * Text of Tax Rate not in Tax Rule form
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Tax rate is absent in tax rule from.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateSuccessDeleteMessage.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateSuccessDeleteMessage.php
new file mode 100644
index 00000000000..914ea56e283
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AssertTaxRateSuccessDeleteMessage.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Tax\Test\Constraint;
+
+use Mtf\Constraint\AbstractConstraint;
+use Magento\Tax\Test\Page\Adminhtml\TaxRateIndex;
+
+/**
+ * Class AssertTaxRateSuccessDeleteMessage
+ */
+class AssertTaxRateSuccessDeleteMessage extends AbstractConstraint
+{
+    const SUCCESS_DELETE_MESSAGE = 'The tax rate has been deleted.';
+
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'high';
+
+    /**
+     * Assert that success delete message is displayed after tax rate deleted
+     *
+     * @param TaxRateIndex $taxRateIndex
+     * @return void
+     */
+    public function processAssert(TaxRateIndex $taxRateIndex)
+    {
+        $actualMessage = $taxRateIndex->getMessagesBlock()->getSuccessMessages();
+        \PHPUnit_Framework_Assert::assertEquals(
+            self::SUCCESS_DELETE_MESSAGE,
+            $actualMessage,
+            'Wrong success delete message is displayed.'
+            . "\nExpected: " . self::SUCCESS_DELETE_MESSAGE
+            . "\nActual: " . $actualMessage
+        );
+    }
+
+    /**
+     * Text of Deleted Tax Rate Success Message assert
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'Tax rate success delete message is present.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest.php b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest.php
new file mode 100644
index 00000000000..0dc974965c2
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Tax\Test\TestCase;
+
+use Magento\Tax\Test\Fixture\TaxRate;
+use Magento\Tax\Test\Page\Adminhtml\TaxRateIndex;
+use Magento\Tax\Test\Page\Adminhtml\TaxRateNew;
+use Mtf\TestCase\Injectable;
+
+/**
+ * Test creation for delete TaxRateEntity
+ *
+ * Test Flow:
+ * Preconditions:
+ * 1. Create Tax Rate
+ *
+ * Steps:
+ * 1. Log in as default admin user
+ * 2. Go to Stores -> Taxes -> Tax Zones and Rates
+ * 3. Open created tax rate
+ * 4. Click Delete Rate
+ * 5. Perform all assertions
+ *
+ * @group Tax_(CS)
+ * @ZephyrId MAGETWO-23295
+ */
+class DeleteTaxRateEntityTest extends Injectable
+{
+    /**
+     * Tax Rate grid page
+     *
+     * @var TaxRateIndex
+     */
+    protected $taxRateIndex;
+
+    /**
+     * Tax Rate new/edit page
+     *
+     * @var TaxRateNew
+     */
+    protected $taxRateNew;
+
+    /**
+     * Injection data
+     *
+     * @param TaxRateIndex $taxRateIndex
+     * @param TaxRateNew $taxRateNew
+     * @return void
+     */
+    public function __inject(
+        TaxRateIndex $taxRateIndex,
+        TaxRateNew $taxRateNew
+    ) {
+        $this->taxRateIndex = $taxRateIndex;
+        $this->taxRateNew = $taxRateNew;
+    }
+
+    /**
+     * Delete Tax Rate Entity test
+     *
+     * @param TaxRate $taxRate
+     * @return void
+     */
+    public function testDeleteTaxRate(TaxRate $taxRate)
+    {
+        // Precondition
+        $taxRate->persist();
+
+        // Steps
+        $filter = [
+            'code' => $taxRate->getCode(),
+        ];
+        $this->taxRateIndex->open();
+        $this->taxRateIndex->getTaxRateGrid()->searchAndOpen($filter);
+        $this->taxRateNew->getFormPageActions()->delete();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest/testDeleteTaxRate.csv b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest/testDeleteTaxRate.csv
new file mode 100644
index 00000000000..ada912bbcb1
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/DeleteTaxRateEntityTest/testDeleteTaxRate.csv
@@ -0,0 +1,2 @@
+"taxRate/dataSet";"constraint"
+"default";"assertTaxRateSuccessDeleteMessage, assertTaxRateNotInGrid, assertTaxRateNotInTaxRule"
\ No newline at end of file
diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/etc/global/constraint.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/etc/global/constraint.xml
index 3e353cf8a61..38f3260709d 100644
--- a/dev/tests/functional/tests/app/Magento/Tax/Test/etc/global/constraint.xml
+++ b/dev/tests/functional/tests/app/Magento/Tax/Test/etc/global/constraint.xml
@@ -95,4 +95,26 @@
             <taxRule class="Magento\Tax\Test\Fixture\TaxRule" />
         </require>
     </assertTaxRuleNotInGrid>
+    <assertTaxRateSuccessDeleteMessage module="Magento_Tax">
+        <severeness>high</severeness>
+        <require>
+            <taxRateIndex class="Magento\Tax\Test\Page\Adminhtml\TaxRateIndex" />
+        </require>
+    </assertTaxRateSuccessDeleteMessage>
+    <assertTaxRateNotInGrid module="Magento_Tax">
+        <severeness>high</severeness>
+        <require>
+            <taxRateIndex class="Magento\Tax\Test\Page\Adminhtml\TaxRateIndex" />
+            <taxRate class="Magento\Tax\Test\Fixture\TaxRate" />
+        </require>
+    </assertTaxRateNotInGrid>
+    <assertTaxRateNotInTaxRule module="Magento_Tax">
+        <severeness>high</severeness>
+        <require>
+            <taxRate class="Magento\Tax\Test\Fixture\TaxRate" />
+            <taxRule class="Magento\Tax\Test\Fixture\TaxRule" />
+            <taxRuleIndex class="Magento\Tax\Test\Page\Adminhtml\TaxRuleIndex" />
+            <taxRuleNew class="Magento\Tax\Test\Page\Adminhtml\TaxRuleNew" />
+        </require>
+    </assertTaxRateNotInTaxRule>
 </constraint>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Grid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php
similarity index 81%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Grid.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php
index 499d5a6cc42..35b318a1b3f 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Grid.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php
@@ -22,24 +22,27 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
-namespace Magento\UrlRewrite\Test\Block\Catalog\Category;
+namespace Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category;
 
-use Magento\Backend\Test\Block\Widget\Grid as GridInterface;
+use Magento\Backend\Test\Block\Widget\Grid as ParentGrid;
 
 /**
  * Class Grid
  * URL Redirect grid
  */
-class Grid extends GridInterface
+class Grid extends ParentGrid
 {
     /**
      * Filters array mapping
      *
-     * @var array $filters
+     * @var array
      */
     protected $filters = [
         'request_path' => [
             'selector' => '#urlrewriteGrid_filter_request_path'
+        ],
+        'id_path' => [
+            'selector' => '#urlrewriteGrid_filter_id_path'
         ]
     ];
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Tree.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Tree.php
similarity index 72%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Tree.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Tree.php
index d8f6a933844..6bfad738887 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Category/Tree.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Tree.php
@@ -22,7 +22,7 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
-namespace Magento\UrlRewrite\Test\Block\Catalog\Category;
+namespace Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category;
 
 use Mtf\Block\Block;
 use Mtf\Client\Element\Locator;
@@ -30,17 +30,34 @@ use Mtf\Client\Element\Locator;
 /**
  * Class Tree
  * Categories tree block
- *
  */
 class Tree extends Block
 {
+    /**
+     * Locator value for  skip category button
+     *
+     * @var string
+     */
+    protected $skipCategoryButton = '[data-ui-id="urlrewrite-catalog-product-edit-skip-categories"]';
+
     /**
      * Select category by its name
      *
      * @param string $categoryName
+     * @return void
      */
     public function selectCategory($categoryName)
     {
         $this->_rootElement->find("//a[contains(text(),'{$categoryName}')]", Locator::SELECTOR_XPATH)->click();
     }
+
+    /**
+     * Skip category selection
+     *
+     * @return void
+     */
+    public function skipCategorySelection()
+    {
+        $this->_rootElement->find($this->skipCategoryButton, Locator::SELECTOR_CSS)->click();
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Edit/Form.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Edit/Form.php
similarity index 94%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Edit/Form.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Edit/Form.php
index bb35b4366b9..f4489b10a08 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Edit/Form.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Edit/Form.php
@@ -22,7 +22,7 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
-namespace Magento\UrlRewrite\Test\Block\Catalog\Edit;
+namespace Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Edit;
 
 use Magento\Backend\Test\Block\Widget\Form as FormWidget;
 
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Edit/Form.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Edit/Form.xml
similarity index 100%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Edit/Form.xml
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Edit/Form.xml
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Product/Grid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Product/Grid.php
similarity index 69%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Product/Grid.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Product/Grid.php
index 6ababae7f9a..5f9f7baeece 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Catalog/Product/Grid.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Product/Grid.php
@@ -22,37 +22,41 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
-namespace Magento\UrlRewrite\Test\Block\Catalog\Product;
+namespace Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Product;
 
-use Magento\Backend\Test\Block\Widget\Grid as GridInterface;
+use Magento\Backend\Test\Block\Widget\Grid as ParentGrid;
 
 /**
  * Class Grid
  * Product grid
- *
  */
-class Grid extends GridInterface
+class Grid extends ParentGrid
 {
+    /**
+     * An element locator which allows to select entities in grid
+     *
+     * @var string
+     */
+    protected $selectItem = 'tbody tr .col-entity_id';
+
+    /**
+     * Locator value for link in action column
+     *
+     * @var string
+     */
+    protected $editLink = 'td.col-name';
+
     /**
      * Filters array mapping
      *
      * @var array
      */
-    protected $filters = array(
-        'id' => array(
+    protected $filters = [
+        'id' => [
             'selector' => '[id=productGrid_product_filter_entity_id]',
-        ),
-        'sku' => array(
+        ],
+        'sku' => [
             'selector' => '[id=productGrid_product_filter_sku]',
-        ),
-    );
-
-    /**
-     * Initialize block elements
-     */
-    protected function _init()
-    {
-        parent::_init();
-        $this->selectItem = 'tbody tr .col-entity_id';
-    }
+        ],
+    ];
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Cms/Page/Grid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Cms/Page/Grid.php
new file mode 100644
index 00000000000..efe4eafe95e
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Cms/Page/Grid.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\UrlRewrite\Test\Block\Adminhtml\Cms\Page;
+
+use Magento\Backend\Test\Block\Widget\Grid as ParentGrid;
+
+/**
+ * Class Grid
+ * URL Redirect grid
+ */
+class Grid extends ParentGrid
+{
+    /**
+     * Locator value for link in action column
+     *
+     * @var string
+     */
+    protected $editLink = 'td.col-title';
+
+    /**
+     * Filters array mapping
+     *
+     * @var array
+     */
+    protected $filters = [
+        'title' => [
+            'selector' => '#cmsPageGrid_filter_title'
+        ]
+    ];
+}
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Selector.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Selector.php
similarity index 96%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Selector.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Selector.php
index 4d19b92fdef..d55a83a9a44 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Selector.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Selector.php
@@ -23,7 +23,7 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
-namespace Magento\UrlRewrite\Test\Block;
+namespace Magento\UrlRewrite\Test\Block\Adminhtml;
 
 use Mtf\Block\Block;
 use Mtf\Client\Element\Locator;
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCmsPageRedirect.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCmsPageRedirect.php
new file mode 100644
index 00000000000..8a8988192c5
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCmsPageRedirect.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\UrlRewrite\Test\Constraint;
+
+use Mtf\Client\Browser;
+use Mtf\Constraint\AbstractConstraint;
+use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
+use Magento\Cms\Test\Fixture\CmsPage;
+
+/**
+ * Class AssertUrlRewriteCmsPageRedirect
+ * Assert that created CMS Page URL Redirect lead to appropriate page in frontend
+ */
+class AssertUrlRewriteCmsPageRedirect extends AbstractConstraint
+{
+    /**
+     * Constraint severeness
+     *
+     * @var string
+     */
+    protected $severeness = 'low';
+
+    /**
+     * URL for CMS Page
+     *
+     * @var string
+     */
+    protected $url = 'cms/page/view/page_id/';
+
+    /**
+     * Assert that created CMS Page URL Redirect lead to appropriate page in frontend
+     *
+     * @param UrlRewrite $urlRewrite
+     * @param CmsPage $cmsPage
+     * @param Browser $browser
+     * @return void
+     */
+    public function processAssert(
+        UrlRewrite $urlRewrite,
+        CmsPage $cmsPage,
+        Browser $browser
+    ) {
+        $browser->open($_ENV['app_frontend_url'] . $urlRewrite->getRequestPath());
+        $url = $urlRewrite->getOptions() == 'No'
+            ? $urlRewrite->getRequestPath()
+            : $this->url . $cmsPage->getPageId();
+
+        \PHPUnit_Framework_Assert::assertEquals(
+            $browser->getUrl(),
+            $_ENV['app_frontend_url'] . $url,
+            'URL rewrite CMS Page redirect false.'
+            . "\nExpected: " . $_ENV['app_frontend_url'] . $url
+            . "\nActual: " . $browser->getUrl()
+        );
+    }
+
+    /**
+     * URL Redirect lead to appropriate page in frontend
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return 'URL Redirect lead to appropriate page in frontend.';
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertProductUrlAvailableOnTheFront.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductRedirect.php
similarity index 82%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertProductUrlAvailableOnTheFront.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductRedirect.php
index 298b499c219..12a84e1d840 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertProductUrlAvailableOnTheFront.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductRedirect.php
@@ -28,12 +28,13 @@ use Magento\Catalog\Test\Page\Product\CatalogProductView;
 use Mtf\Client\Browser;
 use Mtf\Constraint\AbstractConstraint;
 use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
+use Mtf\Fixture\InjectableFixture;
 
 /**
- * Class AssertProductUrlAvailableOnTheFront
+ * Class AssertUrlRewriteProductRedirect
  * Assert that product available by new URL on the front
  */
-class AssertProductUrlAvailableOnTheFront extends AbstractConstraint
+class AssertUrlRewriteProductRedirect extends AbstractConstraint
 {
     /**
      * Constraint severeness
@@ -47,20 +48,25 @@ class AssertProductUrlAvailableOnTheFront extends AbstractConstraint
      *
      * @param UrlRewrite $urlRewrite
      * @param CatalogProductView $catalogProductView
+     * @param InjectableFixture $product
      * @param Browser $browser
      * @return void
      */
     public function processAssert(
         UrlRewrite $urlRewrite,
         CatalogProductView $catalogProductView,
-        Browser $browser
+        Browser $browser,
+        InjectableFixture $product = null
     ) {
         $browser->open($_ENV['app_frontend_url'] . $urlRewrite->getRequestPath());
+        if ($product === null) {
+            $product = $urlRewrite->getDataFieldConfig('id_path')['source']->getEntity();
+        }
         \PHPUnit_Framework_Assert::assertEquals(
             $catalogProductView->getTitleBlock()->getTitle(),
-            $urlRewrite->getDataFieldConfig('product_id')['source']->getProduct()->getName(),
+            $product->getName(),
             'URL rewrite product redirect false.'
-            . "\nExpected: " . $urlRewrite->getDataFieldConfig('product_id')['source']->getProduct()->getName()
+            . "\nExpected: " . $product->getName()
             . "\nActual: " . $catalogProductView->getTitleBlock()->getTitle()
         );
     }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.php
index 8ed995d48c5..8b115150414 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.php
@@ -54,12 +54,7 @@ class UrlRewrite extends InjectableFixture
     protected $id_path = [
         'attribute_code' => 'id_path',
         'backend_type' => 'virtual',
-    ];
-
-    protected $product_id = [
-        'attribute_code' => 'product_id',
-        'backend_type' => 'virtual',
-        'source' => 'Magento\UrlRewrite\Test\Fixture\UrlRewrite\ProductId',
+        'source' => 'Magento\UrlRewrite\Test\Fixture\UrlRewrite\IdPath',
     ];
 
     protected $store_id = [
@@ -102,11 +97,6 @@ class UrlRewrite extends InjectableFixture
         return $this->getData('id_path');
     }
 
-    public function getProductId()
-    {
-        return $this->getData('product_id');
-    }
-
     public function getStoreId()
     {
         return $this->getData('store_id');
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.xml
index 88247b3c1a8..20e71d8cb3d 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite.xml
@@ -37,11 +37,8 @@
         <id_path>
             <attribute_code>id_path</attribute_code>
             <backend_type>virtual</backend_type>
+            <source>Magento\UrlRewrite\Test\Fixture\UrlRewrite\IdPath</source>
         </id_path>
-        <product_id>
-            <attribute_code>product_id</attribute_code>
-            <backend_type>virtual</backend_type>
-        </product_id>
         <store_id>
             <attribute_code>store_id</attribute_code>
             <backend_type>virtual</backend_type>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/ProductId.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/IdPath.php
similarity index 74%
rename from dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/ProductId.php
rename to dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/IdPath.php
index 17c31ee4ab9..d67d9cc7dc0 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/ProductId.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/IdPath.php
@@ -28,10 +28,10 @@ use Mtf\Fixture\FixtureFactory;
 use Mtf\Fixture\FixtureInterface;
 
 /**
- * Class ProductId
- * Prepare product
+ * Class IdPath
+ * Prepare ID Path
  */
-class ProductId implements FixtureInterface
+class IdPath implements FixtureInterface
 {
     /**
      * Resource data
@@ -41,11 +41,11 @@ class ProductId implements FixtureInterface
     protected $data;
 
     /**
-     * Return product
+     * Return category
      *
      * @var FixtureInterface
      */
-    protected $product;
+    protected $entity;
 
     /**
      * @param FixtureFactory $fixtureFactory
@@ -55,14 +55,19 @@ class ProductId implements FixtureInterface
     public function __construct(FixtureFactory $fixtureFactory, array $params, array $data = [])
     {
         $this->params = $params;
-        $explodeValue = explode('::', $data['dataSet']);
+        if (!isset($data['entity'])) {
+            $this->data = array_shift($data);
+            return;
+        }
+        preg_match('`%(.*?)%`', $data['entity'], $dataSet);
+        $explodeValue = explode('::', $dataSet[1]);
         if (!empty($explodeValue) && count($explodeValue) > 1) {
             /** @var FixtureInterface $fixture */
-            $this->product = $fixtureFactory->createByCode($explodeValue[0], ['dataSet' => $explodeValue[1]]);
-            $this->product->persist();
-            $this->data =  $this->product->getId();
+            $this->entity = $fixtureFactory->createByCode($explodeValue[0], ['entity' => $explodeValue[1]]);
+            $this->entity->persist();
+            $this->data = preg_replace('`(%.*?%)`', $this->entity->getId(), $data['entity']);
         } else {
-            $this->data = strval($data['dataSet']);
+            $this->data = strval($data['entity']);
         }
     }
 
@@ -98,12 +103,12 @@ class ProductId implements FixtureInterface
     }
 
     /**
-     * Return product
+     * Return entity
      *
      * @return FixtureInterface
      */
-    public function getProduct()
+    public function getEntity()
     {
-        return $this->product;
+        return $this->entity;
     }
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/Curl.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/Curl.php
index b34e502aac8..fdd1c3eab70 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/Curl.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/Curl.php
@@ -42,7 +42,7 @@ class Curl extends AbstractCurl implements UrlRewriteInterface
      *
      * @var array
      */
-    protected $dataMapping = [
+    protected $mappingData = [
         'store_id' => ['Default Store View' => 1],
         'options' => [
             'Temporary (302)' => 'R',
@@ -68,7 +68,7 @@ class Curl extends AbstractCurl implements UrlRewriteInterface
     public function persist(FixtureInterface $fixture = null)
     {
         $url = $_ENV['app_backend_url'] . $this->url . $fixture->getIdPath();
-        $data = $this->prepareData($fixture->getData());
+        $data = $this->replaceMappingData($fixture->getData());
         $curl = new BackendDecorator(new CurlTransport(), new Config());
         $curl->write(CurlInterface::POST, $url, '1.0', array(), $data);
         $response = $curl->read();
@@ -78,20 +78,4 @@ class Curl extends AbstractCurl implements UrlRewriteInterface
         }
         $curl->close();
     }
-
-    /**
-     * Prepare data
-     *
-     * @param array $data
-     * @return array
-     */
-    protected function prepareData(array $data)
-    {
-        foreach ($data as $key => $value) {
-            if (isset($this->dataMapping[$key])) {
-                $data[$key] = $this->dataMapping[$key][$value];
-            }
-        }
-        return $data;
-    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/UrlRewriteInterface.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/UrlRewriteInterface.php
index 80dccd6f26f..8e86e6795ff 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/UrlRewriteInterface.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Handler/UrlRewrite/UrlRewriteInterface.php
@@ -31,5 +31,5 @@ use Mtf\Handler\HandlerInterface;
  */
 interface UrlRewriteInterface extends HandlerInterface
 {
-   //
+    //
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.php
index 3ace1a1a09f..0ab76c1f45b 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.php
@@ -36,13 +36,13 @@ class UrlrewriteEdit extends BackendPage
     protected $_blocks = [
         'treeBlock' => [
             'name' => 'treeBlock',
-            'class' => 'Magento\UrlRewrite\Test\Block\Catalog\Category\Tree',
-            'locator' => '[data-ui-id="category-selector"]',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Tree',
+            'locator' => '[id="page:main-container"]',
             'strategy' => 'css selector',
         ],
         'formBlock' => [
             'name' => 'formBlock',
-            'class' => 'Magento\UrlRewrite\Test\Block\Catalog\Edit\Form',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Edit\Form',
             'locator' => '#edit_form',
             'strategy' => 'css selector',
         ],
@@ -52,12 +52,6 @@ class UrlrewriteEdit extends BackendPage
             'locator' => '#messages .messages',
             'strategy' => 'css selector',
         ],
-        'buttonBlock' => [
-            'name' => 'buttonBlock',
-            'class' => 'Magento\Backend\Test\Block\Widget\Form',
-            'locator' => '#messages .messages',
-            'strategy' => 'css selector',
-        ],
         'pageMainActions' => [
             'name' => 'pageMainActions',
             'class' => 'Magento\Backend\Test\Block\FormPageActions',
@@ -66,20 +60,26 @@ class UrlrewriteEdit extends BackendPage
         ],
         'productGridBlock' => [
             'name' => 'productGridBlock',
-            'class' => 'Magento\UrlRewrite\Test\Block\Catalog\Product\Grid',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Product\Grid',
             'locator' => '[id="productGrid"]',
             'strategy' => 'css selector',
         ],
         'urlRewriteTypeSelectorBlock' => [
             'name' => 'urlRewriteTypeSelectorBlock',
-            'class' => 'Magento\UrlRewrite\Test\Block\Selector',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Selector',
             'locator' => '[data-ui-id="urlrewrite-type-selector"]',
             'strategy' => 'css selector',
         ],
+        'cmsGridBlock' => [
+            'name' => 'gridBlock',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Cms\Page\Grid',
+            'locator' => '#cmsPageGrid',
+            'strategy' => 'css selector',
+        ],
     ];
 
     /**
-     * @return \Magento\UrlRewrite\Test\Block\Catalog\Category\Tree
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Tree
      */
     public function getTreeBlock()
     {
@@ -87,7 +87,7 @@ class UrlrewriteEdit extends BackendPage
     }
 
     /**
-     * @return \Magento\UrlRewrite\Test\Block\Catalog\Edit\Form
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Edit\Form
      */
     public function getFormBlock()
     {
@@ -102,14 +102,6 @@ class UrlrewriteEdit extends BackendPage
         return $this->getBlockInstance('messagesBlock');
     }
 
-    /**
-     * @return \Magento\Backend\Test\Block\Widget\Form
-     */
-    public function getButtonBlock()
-    {
-        return $this->getBlockInstance('buttonBlock');
-    }
-
     /**
      * @return \Magento\Backend\Test\Block\FormPageActions
      */
@@ -119,7 +111,7 @@ class UrlrewriteEdit extends BackendPage
     }
 
     /**
-     * @return \Magento\UrlRewrite\Test\Block\Catalog\Product\Grid
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Product\Grid
      */
     public function getProductGridBlock()
     {
@@ -127,10 +119,18 @@ class UrlrewriteEdit extends BackendPage
     }
 
     /**
-     * @return \Magento\UrlRewrite\Test\Block\Selector
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Selector
      */
     public function getUrlRewriteTypeSelectorBlock()
     {
         return $this->getBlockInstance('urlRewriteTypeSelectorBlock');
     }
+
+    /**
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Cms\Page\Grid
+     */
+    public function getCmsGridBlock()
+    {
+        return $this->getBlockInstance('cmsGridBlock');
+    }
 }
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.xml
index 0eea42abf33..35c41fe1518 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteEdit.xml
@@ -26,13 +26,13 @@
 <page mca="admin/urlrewrite/edit">
     <block>
         <name>treeBlock</name>
-        <class>Magento\UrlRewrite\Test\Block\Catalog\Category\Tree</class>
-        <locator>[data-ui-id="category-selector"]</locator>
+        <class>Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Tree</class>
+        <locator>[id="page:main-container"]</locator>
         <strategy>css selector</strategy>
     </block>
     <block>
         <name>formBlock</name>
-        <class>Magento\UrlRewrite\Test\Block\Catalog\Edit\Form</class>
+        <class>Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Edit\Form</class>
         <locator>#edit_form</locator>
         <strategy>css selector</strategy>
     </block>
@@ -42,12 +42,6 @@
         <locator>#messages .messages</locator>
         <strategy>css selector</strategy>
     </block>
-    <block>
-        <name>buttonBlock</name>
-        <class>Magento\Backend\Test\Block\Widget\Form</class>
-        <locator>#messages .messages</locator>
-        <strategy>css selector</strategy>
-    </block>
     <block>
         <name>pageMainActions</name>
         <class>Magento\Backend\Test\Block\FormPageActions</class>
@@ -56,20 +50,20 @@
     </block>
     <block>
         <name>productGridBlock</name>
-        <class>Magento\UrlRewrite\Test\Block\Catalog\Product\Grid</class>
+        <class>Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Product\Grid</class>
         <locator>[id="productGrid"]</locator>
         <strategy>css selector</strategy>
     </block>
     <block>
         <name>urlRewriteTypeSelectorBlock</name>
-        <class>Magento\UrlRewrite\Test\Block\Selector</class>
+        <class>Magento\UrlRewrite\Test\Block\Adminhtml\Selector</class>
         <locator>[data-ui-id="urlrewrite-type-selector"]</locator>
         <strategy>css selector</strategy>
     </block>
     <block>
-        <name>urlRewriteTypeSelectorBlock</name>
-        <class>Magento\UrlRewrite\Test\Block\Selector</class>
-        <locator>[data-ui-id="urlrewrite-type-selector"]</locator>
+        <name>cmsGridBlock</name>
+        <class>Magento\UrlRewrite\Test\Block\Cms\Page\Grid</class>
+        <locator>#cmsPageGrid</locator>
         <strategy>css selector</strategy>
     </block>
 </page>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.php
index 452fbb6c426..900ecad85e1 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.php
@@ -42,7 +42,7 @@ class UrlrewriteIndex extends BackendPage
         ],
         'urlRedirectGrid' => [
             'name' => 'urlRedirectGrid',
-            'class' => 'Magento\UrlRewrite\Test\Block\Catalog\Category\Grid',
+            'class' => 'Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Grid',
             'locator' => '#urlrewriteGrid',
             'strategy' => 'css selector',
         ],
@@ -63,7 +63,7 @@ class UrlrewriteIndex extends BackendPage
     }
 
     /**
-     * @return \Magento\UrlRewrite\Test\Block\Catalog\Category\Grid
+     * @return \Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Grid
      */
     public function getUrlRedirectGrid()
     {
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.xml
index 65cd2bcee95..566d5370b38 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Page/Adminhtml/UrlrewriteIndex.xml
@@ -32,7 +32,7 @@
     </block>
     <block>
         <name>urlRedirectGrid</name>
-        <class>Magento\UrlRewrite\Test\Block\Catalog\Category\Grid</class>
+        <class>Magento\UrlRewrite\Test\Block\Adminhtml\Catalog\Category\Grid</class>
         <locator>#urlrewriteGrid</locator>
         <strategy>css selector</strategy>
     </block>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest.php
new file mode 100644
index 00000000000..3bb8056bd21
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\UrlRewrite\Test\TestCase;
+
+use Magento\UrlRewrite\Test\Page\Adminhtml\EditCmsPage;
+use Mtf\TestCase\Injectable;
+use Magento\Cms\Test\Fixture\CmsPage;
+use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteEdit;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteIndex;
+
+/**
+ * Test Creation for CreateCmsPageRewriteEntity
+ *
+ * Test Flow:
+ *
+ * Preconditions
+ * 1. Create CMS-Page
+ *
+ * Steps
+ * 1. Login to backend as Admin
+ * 2. Go to the Marketing-> SEO & Search->URL Redirects
+ * 3. Click "Add Url Rewrite" button
+ * 4. Select "For CMS Page" in Create URL Rewrite dropdown
+ * 5. Select CMS page from preconditions in grid
+ * 6. Fill data according to data set
+ * 7. Save Rewrite
+ * 8. Perform all assertions
+ *
+ * @group URL_Rewrites_(PS)
+ * @ZephyrId MAGETWO-24847
+ */
+class CreateCmsPageRewriteEntityTest extends Injectable
+{
+    /**
+     * Url rewrite index page
+     *
+     * @var UrlrewriteIndex
+     */
+    protected $urlRewriteIndex;
+
+    /**
+     * Url rewrite edit page
+     *
+     * @var UrlrewriteEdit
+     */
+    protected $urlRewriteEdit;
+
+    /**
+     * Inject pages
+     *
+     * @param UrlrewriteIndex $urlRewriteIndex
+     * @param UrlrewriteEdit $urlRewriteEdit
+     * @return void
+     */
+    public function __inject(
+        UrlrewriteIndex $urlRewriteIndex,
+        UrlrewriteEdit $urlRewriteEdit
+    ) {
+        $this->urlRewriteIndex = $urlRewriteIndex;
+        $this->urlRewriteEdit = $urlRewriteEdit;
+    }
+
+    /**
+     * Create CMS page rewrites
+     *
+     * @param CmsPage $cmsPage
+     * @param UrlRewrite $urlRewrite
+     * @return void
+     */
+    public function testCmsPageRewrite(CmsPage $cmsPage, UrlRewrite $urlRewrite)
+    {
+        //Preconditions
+        $cmsPage->persist();
+        //Steps
+        $this->urlRewriteIndex->open();
+        $this->urlRewriteIndex->getPageActionsBlock()->addNew();
+        $this->urlRewriteEdit->getUrlRewriteTypeSelectorBlock()->selectType('For CMS page');
+        $filter = ['title' => $cmsPage->getTitle()];
+        $this->urlRewriteEdit->getCmsGridBlock()->searchAndOpen($filter);
+        $this->urlRewriteEdit->getFormBlock()->fill($urlRewrite);
+        $this->urlRewriteEdit->getPageMainActions()->save();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest/testCmsPageRewrite.csv b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest/testCmsPageRewrite.csv
new file mode 100644
index 00000000000..7edd14cc928
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCmsPageRewriteEntityTest/testCmsPageRewrite.csv
@@ -0,0 +1,5 @@
+"cmsPage/dataSet";"urlRewrite/data/store_id";"urlRewrite/data/request_path";"urlRewrite/data/options";"urlRewrite/data/description";"isRequired";"constraint"
+"default";"-";"request_path%isolation%";"No";"test_description_default";"Yes";"assertUrlRewriteSaveMessage, assertUrlRewriteCmsPageRedirect"
+"default";"-";"request_path%isolation%.html";"Temporary (302)";"test description_302";"Yes";"assertUrlRewriteSaveMessage, assertUrlRewriteCmsPageRedirect"
+"default";"-";"request_path%isolation%.htm";"Permanent (301)";"test description_301";"Yes";"assertUrlRewriteSaveMessage, assertUrlRewriteCmsPageRedirect"
+"default";"-";"request_path%isolation%.aspx";"Permanent (301)";"test description_%isolation%";"Yes";"assertUrlRewriteSaveMessage, assertUrlRewriteCmsPageRedirect"
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.php
new file mode 100644
index 00000000000..14d53054878
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\UrlRewrite\Test\TestCase;
+
+use Magento\Catalog\Test\Fixture\CatalogProductSimple;
+use Mtf\TestCase\Injectable;
+use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteIndex;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteEdit;
+
+/**
+ * Test Creation for Product URL Rewrites Entity
+ *
+ * Test Flow:
+ * Preconditions:
+ * 1. Create custom storeView
+ * 2. Create simple product
+ *
+ * Steps:
+ * 1. Open Backend
+ * 2. Go to Marketing->Url Redirects
+ * 3. Click "Add URL Rewrite" button
+ * 4. Select "For Product" from  "Create URL Rewrite:" dropdown
+ * 5. Select created early product
+ * 6. Click "Skip Category Selection" button
+ * 7. Fill data according to dataSet
+ * 8. Perform all assertions
+ *
+ * @group URL_Rewrites_(PS)
+ * @ZephyrId MAGETWO-25150
+ */
+class CreateProductUrlRewriteEntityTest extends Injectable
+{
+    /**
+     * Url rewrite index page
+     *
+     * @var UrlrewriteIndex
+     */
+    protected $urlRewriteIndex;
+
+    /**
+     * Url rewrite edit page
+     *
+     * @var UrlrewriteEdit
+     */
+    protected $urlRewriteEdit;
+
+    /**
+     * Prepare pages
+     *
+     * @param UrlrewriteIndex $urlRewriteIndex
+     * @param UrlrewriteEdit $urlRewriteEdit
+     * @return void
+     */
+    public function __inject(UrlrewriteIndex $urlRewriteIndex, UrlrewriteEdit $urlRewriteEdit)
+    {
+        $this->urlRewriteIndex = $urlRewriteIndex;
+        $this->urlRewriteEdit = $urlRewriteEdit;
+    }
+
+    /**
+     * Create product URL Rewrite
+     *
+     * @param CatalogProductSimple $product
+     * @param UrlRewrite $urlRewrite
+     * @return void
+     */
+    public function testProductUrlRewrite(CatalogProductSimple $product, UrlRewrite $urlRewrite)
+    {
+        //Precondition
+        $product->persist();
+        $filter = ['id' => $product->getId()];
+        //Steps
+        $this->urlRewriteIndex->open();
+        $this->urlRewriteIndex->getPageActionsBlock()->addNew();
+        $this->urlRewriteEdit->getUrlRewriteTypeSelectorBlock()->selectType('For product');
+        $this->urlRewriteEdit->getProductGridBlock()->searchAndOpen($filter);
+        $this->urlRewriteEdit->getTreeBlock()->skipCategorySelection();
+        $this->urlRewriteEdit->getFormBlock()->fill($urlRewrite);
+        $this->urlRewriteEdit->getPageMainActions()->save();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest/testProductUrlRewrite.csv b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest/testProductUrlRewrite.csv
new file mode 100644
index 00000000000..5c712f8e5b9
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest/testProductUrlRewrite.csv
@@ -0,0 +1,4 @@
+"product/dataSet";"urlRewrite/data/store_id";"urlRewrite/data/request_path";"urlRewrite/data/options";"urlRewrite/data/description";"constraint"
+"default";"Main Website/Main Website Store/Default Store View";"test_%isolation%.html";"No";"description_%isolation%";"assertUrlRewriteSaveMessage"
+"default";"Main Website/Main Website Store/Default Store View";"test_%isolation%.html";"Temporary (302)";"description_%isolation%";"assertUrlRewriteSaveMessage, assertUrlRewriteProductRedirect"
+"default";"Main Website/Main Website Store/Default Store View";"test_%isolation%.html";"Permanent (301)";"description_%isolation%";"assertUrlRewriteSaveMessage, assertUrlRewriteProductRedirect"
\ No newline at end of file
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.php
new file mode 100644
index 00000000000..02943ff1a55
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\UrlRewrite\Test\TestCase;
+
+use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
+use Mtf\TestCase\Injectable;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteEdit;
+use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteIndex;
+
+/**
+ * Test Creation for Delete Category URL Rewrites Entity
+ *
+ * Test Flow:
+ * Preconditions:
+ * 1. Create category
+ * 2. Create custom category UrlRewrite
+ *
+ * Steps:
+ * 1. Open Backend
+ * 2. Go to Marketing->URL Redirects
+ * 3. Search and open created URL Redirect
+ * 4. Delete URL Redirect
+ * 5. Perform all assertions
+ *
+ * @group URL_Rewrites_(PS)
+ * @ZephyrId MAGETWO-25086
+ */
+class DeleteCategoryUrlRewriteEntityTest extends Injectable
+{
+    /**
+     * Url rewrite index page
+     *
+     * @var UrlrewriteIndex
+     */
+    protected $urlRewriteIndex;
+
+    /**
+     * Url rewrite edit page
+     *
+     * @var UrlrewriteEdit
+     */
+    protected $urlRewriteEdit;
+
+    /**
+     * Inject pages
+     *
+     * @param UrlrewriteIndex $urlRewriteIndex
+     * @param UrlrewriteEdit $urlRewriteEdit
+     * @return void
+     */
+    public function __inject(UrlrewriteIndex $urlRewriteIndex, UrlrewriteEdit $urlRewriteEdit)
+    {
+        $this->urlRewriteIndex = $urlRewriteIndex;
+        $this->urlRewriteEdit = $urlRewriteEdit;
+    }
+
+    /**
+     * Delete category Url Rewrite
+     *
+     * @param UrlRewrite $urlRewrite
+     * @return void
+     */
+    public function testDeleteCategoryUrlRewrite(UrlRewrite $urlRewrite)
+    {
+        //Precondition
+        $urlRewrite->persist();
+        //Steps
+        $this->urlRewriteIndex->open();
+        if ($urlRewrite->getRequestPath()) {
+            $filter = ['request_path' => $urlRewrite->getRequestPath()];
+        } else {
+            $filter = ['id_path' => $urlRewrite->getIdPath()];
+        }
+        $this->urlRewriteIndex->getUrlRedirectGrid()->searchAndOpen($filter);
+        $this->urlRewriteEdit->getPageMainActions()->delete();
+    }
+}
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest/testDeleteCategoryUrlRewrite.csv b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest/testDeleteCategoryUrlRewrite.csv
new file mode 100644
index 00000000000..d56bf6e4537
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest/testDeleteCategoryUrlRewrite.csv
@@ -0,0 +1,3 @@
+"urlRewrite/data/id_path/entity";"urlRewrite/data/options";"urlRewrite/data/request_path";"constraint"
+"category/%catalogCategory::default%";"No";"-";"assertUrlRewriteDeletedMessage, assertPageByUrlRewriteIsNotFound"
+"category/%catalogCategory::default%";"No";"example%isolation%.html";"assertUrlRewriteDeletedMessage, assertPageByUrlRewriteIsNotFound"
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.php
index 6b7b8baa82b..1c0765dcd66 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.php
@@ -28,7 +28,6 @@ use Magento\Catalog\Test\Fixture\CatalogProductSimple;
 use Magento\UrlRewrite\Test\Fixture\UrlRewrite;
 use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteEdit;
 use Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteIndex;
-use Mtf\Fixture\FixtureFactory;
 use Mtf\TestCase\Injectable;
 
 /**
@@ -66,35 +65,18 @@ class DeleteProductUrlRewriteEntityTest extends Injectable
     protected $urlRewriteEdit;
 
     /**
-     * Prepare dataSets and pages
+     * Prepare pages
      *
-     * @param FixtureFactory $fixtureFactory
      * @param UrlrewriteIndex $urlRewriteIndex
      * @param UrlrewriteEdit $urlRewriteEdit
-     * @return array
+     * @return void
      */
     public function __inject(
-        FixtureFactory $fixtureFactory,
         UrlrewriteIndex $urlRewriteIndex,
         UrlrewriteEdit $urlRewriteEdit
     ) {
-        /** @var CatalogProductSimple $product */
-        $product = $fixtureFactory->createByCode('catalogProductSimple', ['dataSet' => 'product_without_category']);
-        $product->persist();
-
-        /** @var UrlRewrite $productRedirect */
-        $productRedirect = $fixtureFactory->createByCode(
-            'urlRewrite',
-            [
-                'dataSet' => 'default',
-                'data' => ['id_path' => 'product/' . $product->getId()]
-            ]
-        );
-        $productRedirect->persist();
-
         $this->urlRewriteIndex = $urlRewriteIndex;
         $this->urlRewriteEdit = $urlRewriteEdit;
-        return ['productRedirect' => $productRedirect];
     }
 
     /**
@@ -105,6 +87,9 @@ class DeleteProductUrlRewriteEntityTest extends Injectable
      */
     public function testDeleteProductUrlRewrite(UrlRewrite $productRedirect)
     {
+        // Precondition
+        $productRedirect->persist();
+        // Steps
         $this->urlRewriteIndex->open();
         $filter = ['request_path' => $productRedirect->getRequestPath()];
         $this->urlRewriteIndex->getUrlRedirectGrid()->searchAndOpen($filter);
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest/testDeleteProductUrlRewrite.csv b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest/testDeleteProductUrlRewrite.csv
index a0af0ee8456..102c7b7f215 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest/testDeleteProductUrlRewrite.csv
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest/testDeleteProductUrlRewrite.csv
@@ -1,2 +1,2 @@
-"constraint"
-"assertUrlRewriteDeletedMessage, assertUrlRewriteNotInGrid, assertPageByUrlRewriteIsNotFound"
+"productRedirect/dataSet";"productRedirect/data/id_path/entity";"constraint"
+"default";"product/%catalogProductSimple::100_dollar_product%";"assertUrlRewriteDeletedMessage, assertUrlRewriteNotInGrid, assertPageByUrlRewriteIsNotFound"
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateCategoryUrlRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateCategoryUrlRewriteEntityTest.php
index f27c54428de..ae00d41bc8b 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateCategoryUrlRewriteEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateCategoryUrlRewriteEntityTest.php
@@ -89,7 +89,7 @@ class UpdateCategoryUrlRewriteEntityTest extends Injectable
             'urlRewrite',
             [
                 'dataSet' => 'default',
-                'data' => ['id_path' => 'category/' . $category->getId()]
+                'data' => ['id_path' => ['category/' . $category->getId()]]
             ]
         );
         $categoryRedirect->persist();
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.php
index 88e91acf55c..bead2feb702 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.php
@@ -97,7 +97,7 @@ class UpdateProductUrlRewriteEntityTest extends Injectable
             'urlRewrite',
             [
                 'dataSet' => 'default',
-                'data' => ['id_path' => 'product/' . $urlRewrite->getProductId()]
+                'data' => ['id_path' => [$urlRewrite->getIdPath()]]
             ]
         );
         $productRedirect->persist();
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest/testUpdateProductUrlRewrite.csv b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest/testUpdateProductUrlRewrite.csv
index a2149c6d1a8..4a76b6aaf18 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest/testUpdateProductUrlRewrite.csv
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest/testUpdateProductUrlRewrite.csv
@@ -1,3 +1,2 @@
-"urlRewrite/data/product_id/dataSet";"urlRewrite/data/store_id";"urlRewrite/data/request_path";"urlRewrite/data/options";"urlRewrite/data/description";"isRequired";"constraint"
-"catalogProductSimple::100_dollar_product";"Main Website/Main Website Store/Default Store View";"test_%isolation%.html";"Temporary (302)";"description_%isolation%";"Yes";"assertUrlRewriteSaveMessage, assertProductUrlAvailableOnTheFront"
-"catalogProductSimple::100_dollar_product";"Main Website/Main Website Store/Custom Store View";"test_%isolation%.php";"No";"description_%isolation%";"No";"assertUrlRewriteSaveMessage, assertProductUrlAvailableOnTheFront"
\ No newline at end of file
+"urlRewrite/data/id_path/entity";"urlRewrite/data/store_id";"urlRewrite/data/request_path";"urlRewrite/data/options";"urlRewrite/data/description";"isRequired";"constraint"
+"product/%catalogProductSimple::100_dollar_product%";"Main Website/Main Website Store/Default Store View";"test_%isolation%.html";"Temporary (302)";"description_%isolation%";"Yes";"assertUrlRewriteSaveMessage, assertUrlRewriteProductRedirect"
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/constraint.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/constraint.xml
index 60b2f162099..cba450836de 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/constraint.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/constraint.xml
@@ -37,7 +37,7 @@
     <assertUrlRewriteCategoryRedirect module="Magento_UrlRewrite">
         <severeness>low</severeness>
     </assertUrlRewriteCategoryRedirect>
-    <assertProductUrlAvailableOnTheFront module="Magento_UrlRewrite">
+    <assertUrlRewriteProductRedirect module="Magento_UrlRewrite">
         <severeness>low</severeness>
         <require>
             <urlRewrite class="Magento\UrlRewrite\Test\Fixture\UrlRewrite"/>
@@ -45,7 +45,7 @@
             <product class="Mtf\Fixture\FixtureInterface"/>
             <browser class="Mtf\Client\Browser"/>
         </require>
-    </assertProductUrlAvailableOnTheFront>
+    </assertUrlRewriteProductRedirect>
     <assertUrlRewriteDeletedMessage module="Magento_UrlRewrite">
         <severeness>low</severeness>
         <require>
@@ -67,4 +67,12 @@
             <browser class="Mtf\Client\Browser"/>
         </require>
     </assertPageByUrlRewriteIsNotFound>
+    <assertUrlRewriteCmsPageRedirect module="Magento_UrlRewrite">
+        <severeness>low</severeness>
+        <require>
+            <cmsPage class="Magento\Cms\Test\Fixture\CmsPage"/>
+            <urlRewrite class="Magento\UrlRewrite\Test\Fixture\UrlRewrite"/>
+            <browser class="Mtf\Client\Browser"/>
+        </require>
+    </assertUrlRewriteCmsPageRedirect>
 </constraint>
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/page.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/page.xml
index 0e1e33bb730..d8be3b72836 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/page.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/etc/global/page.xml
@@ -34,4 +34,9 @@
         <area>adminhtml</area>
         <class>Magento\UrlRewrite\Test\Page\Adminhtml\UrlrewriteEdit</class>
     </urlrewriteEdit>
+    <editCmsPage>
+        <mca>admin/urlrewrite/edit/cms_page</mca>
+        <area>adminhtml</area>
+        <class>Magento\UrlRewrite\Test\Page\Adminhtml\EditCmsPage</class>
+    </editCmsPage>
 </page>
diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist
index 51962f3c709..2f406070a79 100644
--- a/dev/tests/integration/phpunit.xml.dist
+++ b/dev/tests/integration/phpunit.xml.dist
@@ -40,7 +40,7 @@
     <filter>
         <whitelist addUncoveredFilesFromWhiteList="true">
             <directory suffix=".php">../../../app/code/Magento</directory>
-            <directory suffix=".php">../../../lib/Magento</directory>
+            <directory suffix=".php">../../../lib/internal/Magento</directory>
             <exclude>
                 <directory suffix=".php">../../../app/code/Magento/*/sql</directory>
                 <directory suffix=".php">../../../app/code/Magento/*/data</directory>
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/AbstractTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/AbstractTest.php
index 15ecca48c35..2a13632919c 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/AbstractTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/AbstractTest.php
@@ -26,8 +26,7 @@ namespace Magento\Catalog\Block\Product;
 /**
  * Test class for \Magento\Catalog\Block\Product\Abstract.
  *
- * @magentoDataFixture Magento/Catalog/_files/product_simple.php
- * @magentoDataFixture Magento/Catalog/_files/product_image.php
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
  */
 class AbstractTest extends \PHPUnit_Framework_TestCase
 {
@@ -144,7 +143,7 @@ class AbstractTest extends \PHPUnit_Framework_TestCase
 
     public function testGetImageLabel()
     {
-        $this->assertEquals($this->_product->getName(), $this->_block->getImageLabel());
+        $this->assertEquals('Image Alt Text', $this->_block->getImageLabel());
     }
 
     public function testGetProductUrl()
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php
index 7667a487a68..00e57c0ff30 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php
@@ -119,7 +119,7 @@ class TierpriceTest extends \PHPUnit_Framework_TestCase
         $this->_model->afterLoad($product);
         $price = $product->getTierPrice();
         $this->assertNotEmpty($price);
-        $this->assertEquals(2, count($price));
+        $this->assertEquals(3, count($price));
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php
index e50cb153a33..1b75531f4a9 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php
@@ -111,7 +111,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase
         );
         $product->load(1);
         // fixture
-        $this->assertEquals(2, $this->_model->getTierPriceCount($product));
+        $this->assertEquals(3, $this->_model->getTierPriceCount($product));
     }
 
     public function testGetFormatedTierPrice()
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute.php
new file mode 100644
index 00000000000..8cdc42ac3ff
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/** @var $product \Magento\Catalog\Model\Product */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */
+$attributeSet = $objectManager->create('\Magento\Eav\Model\Entity\Attribute\Set');
+
+$entityType = $objectManager->create('Magento\Eav\Model\Entity\Type')->loadByCode('catalog_product');
+$defaultSetId = $objectManager->create('\Magento\Catalog\Model\Product')->getDefaultAttributeSetid();
+
+$data = [
+    'attribute_set_name' => 'custom attribute set 531',
+    'entity_type_id' => $entityType->getId(),
+    'sort_order' => 200,
+];
+
+$attributeSet->setData($data);
+$attributeSet->validate();
+$attributeSet->save();
+$attributeSet->initFromSkeleton($defaultSetId);
+$attributeSet->save();
+
+$attributeData = array(
+    'entity_type_id' => $entityType->getId(),
+    'attribute_code' => 'funny_image',
+    'frontend_input' => 'media_image',
+    'frontend_label' => 'Funny image',
+    'backend_type' => 'varchar',
+    'is_required' => 0,
+    'is_user_defined' => 1,
+    'attribute_set_id' => $attributeSet->getId(),
+    'attribute_group_id' => $attributeSet->getDefaultGroupId(),
+);
+
+/** @var \Magento\Catalog\Model\Entity\Attribute $attribute */
+$attribute = $objectManager->create('\Magento\Catalog\Model\Entity\Attribute');
+$attribute->setData($attributeData);
+$attribute->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php
new file mode 100644
index 00000000000..fcf22a8366a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/** @var $product \Magento\Catalog\Model\Product */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+$entityType = $objectManager->create('Magento\Eav\Model\Entity\Type')->loadByCode('catalog_product');
+
+// remove attribute
+
+/** @var \Magento\Catalog\Model\Resource\Product\Attribute\Collection $attributeCollection */
+$attributeCollection = $objectManager->create('\Magento\Catalog\Model\Resource\Product\Attribute\Collection');
+$attributeCollection->setFrontendInputTypeFilter('media_image');
+$attributeCollection->setCodeFilter('funny_image');
+$attributeCollection->setEntityTypeFilter($entityType->getId());
+$attributeCollection->setPageSize(1);
+$attributeCollection->load();
+$attribute = $attributeCollection->fetchItem();
+$attribute->delete();
+
+// remove attribute set
+
+/** @var \Magento\Eav\Model\Resource\Entity\Attribute\Set\Collection $attributeSetCollection */
+$attributeSetCollection = $objectManager->create('\Magento\Eav\Model\Resource\Entity\Attribute\Set\Collection');
+$attributeSetCollection->addFilter('attribute_set_name', 'custom attribute set 531');
+$attributeSetCollection->addFilter('entity_type_id', $entityType->getId());
+$attributeSetCollection->setOrder('attribute_set_id'); // descending is default value
+$attributeSetCollection->setPageSize(1);
+$attributeSetCollection->load();
+
+/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */
+$attributeSet = $attributeSetCollection->fetchItem();
+$attributeSet->delete();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices.php
new file mode 100644
index 00000000000..f87b3b5af2c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/** @var $product \Magento\Catalog\Model\Product */
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+    ->create('Magento\Catalog\Model\Product');
+$product->isObjectNew(true);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+    ->setId(1)
+    ->setAttributeSetId(4)
+    ->setWebsiteIds(array(1))
+    ->setName('Simple Product Group Price')
+    ->setSku('simple_with_group_price')
+    ->setPrice(10)
+    ->setWeight(1)
+    ->setShortDescription("Short description")
+    ->setTaxClassId(0)
+    ->setGroupPrice(
+        array(
+            array(
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Service\V1\CustomerGroupServiceInterface::NOT_LOGGED_IN_ID,
+                'price'      => 9,
+            ),
+            array(
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Service\V1\CustomerGroupServiceInterface::CUST_GROUP_ALL,
+                'price'      => 7,
+            ),
+        )
+    )
+    ->setDescription('Description with <b>html tag</b>')
+    ->setMetaTitle('meta title')
+    ->setMetaKeyword('meta keyword')
+    ->setMetaDescription('meta description')
+    ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+    ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+    ->setCategoryIds(array(2))
+    ->setStockData(
+        array(
+            'use_config_manage_stock'   => 1,
+            'qty'                       => 100,
+            'is_qty_decimal'            => 0,
+            'is_in_stock'               => 1,
+        )
+    )
+    ->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices_rollback.php
new file mode 100644
index 00000000000..6ffb72acf9b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_group_prices_rollback.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/** @var \Magento\Framework\Registry $registry */
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var $product \Magento\Catalog\Model\Product */
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product');
+$product->load(1);
+if ($product->getId()) {
+    $product->delete();
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image.php
index a4ba42c127b..a433616ce80 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image.php
@@ -23,11 +23,18 @@
  */
 
 $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-$mediaPath = $objectManager->get('Magento\Framework\App\Filesystem')
-    ->getPath(\Magento\Framework\App\Filesystem::MEDIA_DIR);
-$additionalPath = $objectManager->get('Magento\Catalog\Model\Product\Media\Config')->getBaseMediaPath();
-$dir = $mediaPath . '/' . $additionalPath . '/m/a';
-if (!is_dir($dir)) {
-    mkdir($dir, 0777, true);
-}
-copy(__DIR__ . '/magento_image.jpg', $dir . '/magento_image.jpg');
+/** @var $mediaConfig \Magento\Catalog\Model\Product\Media\Config */
+$mediaConfig = $objectManager->get('Magento\Catalog\Model\Product\Media\Config');
+
+/** @var $mediaDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */
+$mediaDirectory = $objectManager->get('Magento\Framework\App\Filesystem')
+    ->getDirectoryWrite(\Magento\Framework\App\Filesystem::MEDIA_DIR);
+$targetDirPath = $mediaConfig->getBaseMediaPath() . str_replace('/', DIRECTORY_SEPARATOR, '/m/a/');
+$targetTmpDirPath = $mediaConfig->getBaseTmpMediaPath() . str_replace('/', DIRECTORY_SEPARATOR, '/m/a/');
+$mediaDirectory->create($targetDirPath);
+$mediaDirectory->create($targetTmpDirPath);
+
+$targetTmpFilePath = $mediaDirectory->getAbsolutePath() . DIRECTORY_SEPARATOR . $targetTmpDirPath
+    . DIRECTORY_SEPARATOR . 'magento_image.jpg';
+copy(__DIR__ . '/magento_image.jpg', $targetTmpFilePath);
+// Copying the image to target dir is not necessary because during product save, it will be moved there from tmp dir
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php
index d9cd3b8ea96..ab660709210 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php
@@ -50,6 +50,12 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
                 'price_qty'  => 5,
                 'price'      => 5,
             ),
+            array(
+                'website_id' => 0,
+                'cust_group' => \Magento\Customer\Service\V1\CustomerGroupServiceInterface::NOT_LOGGED_IN_ID,
+                'price_qty'  => 3,
+                'price'      => 5,
+            ),
         )
     )
     ->setDescription('Description with <b>html tag</b>')
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image.php
index 925440c9dd4..5e37963002b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image.php
@@ -23,33 +23,20 @@
  */
 
 require __DIR__ . '/product_image.php';
+require __DIR__ . '/product_simple.php';
 
 /** @var $product \Magento\Catalog\Model\Product */
 $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product');
-$product->setTypeId(
-    \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
-)->setId(
-    1
-)->setAttributeSetId(
-    4
-)->setWebsiteIds(
-    array(1)
-)->setName(
-    'Simple Product'
-)->setSku(
-    'simple'
-)->setPrice(
-    10
-)->setDescription(
-    'Description with <b>html tag</b>'
-)->setImage(
-    '/m/a/magento_image.jpg'
-)->setSmallImage(
-    '/m/a/magento_image.jpg'
-)->setThumbnail(
-    '/m/a/magento_image.jpg'
-)->setVisibility(
-    \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH
-)->setStatus(
-    \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
-)->save();
+$product->load(1)
+    ->setStoreId(0)
+    ->setImage('/m/a/magento_image.jpg')
+    ->setSmallImage('/m/a/magento_image.jpg')
+    ->setThumbnail('/m/a/magento_image.jpg')
+    ->setData('media_gallery', array('images' => array(
+        array(
+            'file' => '/m/a/magento_image.jpg',
+            'position' => 1,
+            'label' => 'Image Alt Text',
+            'disabled' => 0,
+        ),
+    )))->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image_rollback.php
index 5d0c89be0cd..6419bc360aa 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_image_rollback.php
@@ -22,4 +22,5 @@
  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
 
+require __DIR__ . '/product_simple_rollback.php';
 require __DIR__ . '/product_image_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/website.php b/dev/tests/integration/testsuite/Magento/Store/_files/website.php
new file mode 100644
index 00000000000..b021dfbc3ca
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/website.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/** @var $website \Magento\Store\Model\Website */
+$website = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Store\Model\Website');
+$website->setData(array('code' => 'test', 'name' => 'Test Website', 'default_group_id' => '1', 'is_default' => '0'));
+$website->save();
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/website_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/website_rollback.php
new file mode 100644
index 00000000000..16b7a94f706
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/website_rollback.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+/** @var \Magento\Framework\Registry $registry */
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Store\Model\Website $website */
+$website = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Store\Model\Website');
+$website->load('test');
+
+if ($website->getId()) {
+    $website->delete();
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
index 30dca15ae97..0b0247dbb6d 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
@@ -46,6 +46,7 @@ class SetupUtil
     ];
 
     const TAX_RATE_TX = 'tax_rate_tx';
+    const TAX_RATE_AUSTIN = 'tax_rate_austin';
     const TAX_RATE_SHIPPING = 'tax_rate_shipping';
     const TAX_STORE_RATE = 'tax_store_rate';
     const REGION_TX = '57';
@@ -69,6 +70,16 @@ class SetupUtil
             ],
             'id' => null,
         ],
+        self::TAX_RATE_AUSTIN => [
+            'data' => [
+                'tax_country_id' => self::COUNTRY_US,
+                'tax_region_id' => self::REGION_TX,
+                'tax_postcode' => self::AUSTIN_POST_CODE,
+                'code' => self::TAX_RATE_AUSTIN,
+                'rate' => '5',
+            ],
+            'id' => null,
+        ],
         self::TAX_RATE_SHIPPING => [
             'data' => [
                 'tax_country_id' => self::COUNTRY_US,
@@ -301,14 +312,12 @@ class SetupUtil
      *
      * @return array
      */
-    protected function getTaxRateIds()
+    protected function getDefaultTaxRateIds()
     {
-        $taxRateIds = [];
-        foreach ($this->taxRates as $taxRateName => $taxRate) {
-            if ($taxRateName != self::TAX_RATE_SHIPPING) {
-                $taxRateIds[] = $taxRate['id'];
-            }
-        }
+        $taxRateIds = [
+            $this->taxRates[self::TAX_RATE_TX]['id'],
+            $this->taxRates[self::TAX_STORE_RATE]['id'],
+        ];
 
         return $taxRateIds;
     }
@@ -353,7 +362,7 @@ class SetupUtil
             'position' => '0',
             'tax_customer_class' => $customerClassIds,
             'tax_product_class' => $this->getProductTaxClassIds(),
-            'tax_rate' => $this->getTaxRateIds(),
+            'tax_rate' => $this->getDefaultTaxRateIds(),
         ];
 
         //Create tax rules
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
index 301e0d8d819..478f2549796 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
@@ -51,6 +51,7 @@ class TaxTest extends \PHPUnit_Framework_TestCase
      * @magentoDataFixture Magento/Tax/_files/tax_classes.php
      * @magentoDataFixture Magento/Customer/_files/customer_group.php
      * @magentoDbIsolation enabled
+     * @magentoAppIsolation enabled
      */
     public function testCollect()
     {
@@ -257,12 +258,12 @@ class TaxTest extends \PHPUnit_Framework_TestCase
      * @param array $quoteData
      * @param array $expectedResults
      * @magentoDbIsolation enabled
+     * @magentoAppIsolation enabled
      * @dataProvider taxDataProvider
      * @return void
      */
     public function testTaxCalculation($configData, $quoteData, $expectedResults)
     {
-        Bootstrap::getInstance()->reinitialize();
         /** @var  \Magento\Framework\ObjectManager $objectManager */
         $objectManager = Bootstrap::getObjectManager();
 
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_tax_before_discount.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_tax_before_discount.php
index 9eaffb5eb75..845791b66da 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_tax_before_discount.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_tax_before_discount.php
@@ -28,6 +28,7 @@ use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
 $taxCalculationData['excluding_tax_apply_tax_before_discount'] = [
     'config_data' => [
         SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0,
             Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::PRODUCT_TAX_CLASS_1,
         ],
         SetupUtil::TAX_RATE_OVERRIDES => [
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_disabled.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_disabled.php
new file mode 100644
index 00000000000..f79dafa544d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_disabled.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+$taxCalculationData['including_tax_cross_border_trade_disabled'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1,
+            Config::CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED => 0,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_UNIT_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 20,
+            SetupUtil::TAX_STORE_RATE => 10,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 9.99,
+                'qty' => 2,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 18.16,
+            'base_subtotal' => 18.16,
+            'subtotal_incl_tax' => 21.80,
+            'base_subtotal_incl_tax' => 21.80,
+            'tax_amount' => 3.64,
+            'base_tax_amount' => 3.64,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 21.80,
+            'base_grand_total' => 21.80,
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 18.16,
+                'base_row_total' => 18.16,
+                'taxable_amount' => 10.90,
+                'base_taxable_amount' => 10.90,
+                'tax_percent' => 20,
+                'price' => 9.08,
+                'base_price' => 9.08,
+                'price_incl_tax' => 10.90,
+                'base_price_incl_tax' => 10.90,
+                'row_total_incl_tax' => 21.80,
+                'base_row_total_incl_tax' => 21.80,
+                'tax_amount' => 3.64,
+                'base_tax_amount' => 3.64,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_enabled.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_enabled.php
new file mode 100644
index 00000000000..c1875cec5a1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_cross_border_trade_enabled.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+$taxCalculationData['including_tax_cross_border_trade_enabled'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1,
+            Config::CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED => 1,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_UNIT_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 20,
+            SetupUtil::TAX_STORE_RATE => 10,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 9.99,
+                'qty' => 2,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 16.64,
+            'base_subtotal' => 16.64,
+            'subtotal_incl_tax' => 19.98,
+            'base_subtotal_incl_tax' => 19.98,
+            'tax_amount' => 3.34,
+            'base_tax_amount' => 3.34,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 19.98,
+            'base_grand_total' => 19.98,
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 16.64,
+                'base_row_total' => 16.64,
+                'taxable_amount' => 9.99,
+                'base_taxable_amount' => 9.99,
+                'tax_percent' => 20,
+                'price' => 8.32,
+                'base_price' => 8.32,
+                'price_incl_tax' => 9.99,
+                'base_price_incl_tax' => 9.99,
+                'row_total_incl_tax' => 19.98,
+                'base_row_total_incl_tax' => 19.98,
+                'tax_amount' => 3.34,
+                'base_tax_amount' => 3.34,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_no.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_no.php
new file mode 100644
index 00000000000..6578ab2fdb1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_no.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+/**
+ * This test case test the scenario where there are two tax rules with different priority
+ * The calculate_subtotal field is off, the second tax rate will be applied on top of first
+ * tax rate. This testcases uses total based calculation.
+ */
+$taxCalculationData['multi_tax_rule_total_calculate_subtotal_no'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_TOTAL_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 7.5,
+            SetupUtil::TAX_RATE_AUSTIN => 5.5,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+            [
+                //tax rule 1 for product
+                'code' => 'Product Tax Rule TX',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_TX],
+                'priority' => 1,
+            ],
+            [
+                //tax rule 2 for product
+                'code' => 'Product Tax Rule AUSTIN',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_AUSTIN],
+                'priority' => 2,
+                'calculate_subtotal' => 0,
+            ],
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+            'tax_postcode' => SetupUtil::AUSTIN_POST_CODE,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 1,
+                'qty' => 10,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 10,
+            'base_subtotal' => 10,
+            'subtotal_incl_tax' => 11.34,
+            'base_subtotal_incl_tax' => 11.34,
+            'tax_amount' => 1.34,
+            'base_tax_amount' => 1.34,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 11.34,
+            'base_grand_total' => 11.34,
+            'applied_taxes' => [
+                SetupUtil::TAX_RATE_TX => [
+                    'percent' => 7.5,
+                    'amount' => 0.75,
+                    'base_amount' => 0.75,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_TX,
+                            'title' => SetupUtil::TAX_RATE_TX,
+                            'percent' => 7.5,
+                        ],
+                    ],
+                ],
+                SetupUtil::TAX_RATE_AUSTIN => [
+                    'percent' => 5.9125,
+                    'amount' => 0.59,
+                    'base_amount' => 0.59,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_AUSTIN,
+                            'title' => SetupUtil::TAX_RATE_AUSTIN,
+                            'percent' => 5.5,
+                        ],
+                    ],
+                ],
+            ],
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 10,
+                'base_row_total' => 10,
+                'taxable_amount' => 10,
+                'base_taxable_amount' => 10,
+                'tax_percent' => 13.4125,
+                'price' => 1,
+                'base_price' => 1,
+                'price_incl_tax' => 1.13,
+                'base_price_incl_tax' => 1.13,
+                'row_total_incl_tax' => 11.34,
+                'base_row_total_incl_tax' => 11.34,
+                'tax_amount' => 1.34,
+                'base_tax_amount' => 1.34,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php
new file mode 100644
index 00000000000..d88e3ddb026
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+/**
+ * This test case test the scenario where there are two tax rules with different priority
+ * The calculate_subtotal field is on, the second tax rate will be applied on subtotal only.
+ * This testcase uses total based calculation.
+ */
+$taxCalculationData['multi_tax_rule_total_calculate_subtotal_yes'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_TOTAL_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 7.5,
+            SetupUtil::TAX_RATE_AUSTIN => 5.5,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+            [
+                //tax rule 1 for product
+                'code' => 'Product Tax Rule TX',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_TX],
+                'priority' => 1,
+            ],
+            [
+                //tax rule 2 for product
+                'code' => 'Product Tax Rule AUSTIN',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_AUSTIN],
+                'priority' => 2,
+                'calculate_subtotal' => 1,
+            ],
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+            'tax_postcode' => SetupUtil::AUSTIN_POST_CODE,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 1,
+                'qty' => 10,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 10,
+            'base_subtotal' => 10,
+            'subtotal_incl_tax' => 11.3,
+            'base_subtotal_incl_tax' => 11.3,
+            'tax_amount' => 1.3,
+            'base_tax_amount' => 1.3,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 11.3,
+            'base_grand_total' => 11.3,
+            'applied_taxes' => [
+                SetupUtil::TAX_RATE_TX => [
+                    'percent' => 7.5,
+                    'amount' => 0.75,
+                    'base_amount' => 0.75,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_TX,
+                            'title' => SetupUtil::TAX_RATE_TX,
+                            'percent' => 7.5,
+                        ],
+                    ],
+                ],
+                SetupUtil::TAX_RATE_AUSTIN => [
+                    'percent' => 5.5,
+                    'amount' => 0.55,
+                    'base_amount' => 0.55,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_AUSTIN,
+                            'title' => SetupUtil::TAX_RATE_AUSTIN,
+                            'percent' => 5.5,
+                        ],
+                    ],
+                ],
+            ],
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 10,
+                'base_row_total' => 10,
+                'taxable_amount' => 10,
+                'base_taxable_amount' => 10,
+                'tax_percent' => 13,
+                'price' => 1,
+                'base_price' => 1,
+                'price_incl_tax' => 1.13,
+                'base_price_incl_tax' => 1.13,
+                'row_total_incl_tax' => 11.3,
+                'base_row_total_incl_tax' => 11.3,
+                'tax_amount' => 1.3,
+                'base_tax_amount' => 1.3,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_no.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_no.php
new file mode 100644
index 00000000000..5e0701ea386
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_no.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+/**
+ * This test case test the scenario where there are two tax rules with different priority
+ * The calculate_subtotal field is off, the second tax rate will be applied on top of first
+ * tax rate. This testcase uses unit based calculation.
+ */
+$taxCalculationData['multi_tax_rule_unit_calculate_subtotal_no'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_UNIT_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 7.5,
+            SetupUtil::TAX_RATE_AUSTIN => 5.5,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+            [
+                //tax rule 1 for product
+                'code' => 'Product Tax Rule TX',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_TX],
+                'priority' => 1,
+            ],
+            [
+                //tax rule 2 for product
+                'code' => 'Product Tax Rule AUSTIN',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_AUSTIN],
+                'priority' => 2,
+                'calculate_subtotal' => 0,
+            ],
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+            'tax_postcode' => SetupUtil::AUSTIN_POST_CODE,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 1,
+                'qty' => 10,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 10,
+            'base_subtotal' => 10,
+            'subtotal_incl_tax' => 11.4,
+            'base_subtotal_incl_tax' => 11.4,
+            'tax_amount' => 1.4,
+            'base_tax_amount' => 1.4,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 11.4,
+            'base_grand_total' => 11.4,
+            'applied_taxes' => [
+                SetupUtil::TAX_RATE_TX => [
+                    'percent' => 7.5,
+                    'amount' => 0.8,
+                    'base_amount' => 0.8,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_TX,
+                            'title' => SetupUtil::TAX_RATE_TX,
+                            'percent' => 7.5,
+                        ],
+                    ],
+                ],
+                SetupUtil::TAX_RATE_AUSTIN => [
+                    'percent' => 5.9125,
+                    'amount' => 0.6,
+                    'base_amount' => 0.6,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_AUSTIN,
+                            'title' => SetupUtil::TAX_RATE_AUSTIN,
+                            'percent' => 5.5,
+                        ],
+                    ],
+                ],
+            ],
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 10,
+                'base_row_total' => 10,
+                'taxable_amount' => 1,
+                'base_taxable_amount' => 1,
+                'tax_percent' => 13.4125,
+                'price' => 1,
+                'base_price' => 1,
+                'price_incl_tax' => 1.14,
+                'base_price_incl_tax' => 1.14,
+                'row_total_incl_tax' => 11.4,
+                'base_row_total_incl_tax' => 11.4,
+                'tax_amount' => 1.4,
+                'base_tax_amount' => 1.4,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_yes.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_yes.php
new file mode 100644
index 00000000000..025ba3b402f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/multi_tax_rule_unit_calculate_subtotal_yes.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+use Magento\Tax\Model\Config;
+use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
+use Magento\Tax\Model\Calculation;
+
+/**
+ * This test case test the scenario where there are two tax rules with different priority
+ * The calculate_subtotal field is on, the second tax rate will be applied on subtotal only.
+ * This testcase uses unit based calculation.
+ */
+$taxCalculationData['multi_tax_rule_unit_calculate_subtotal_yes'] = [
+    'config_data' => [
+        SetupUtil::CONFIG_OVERRIDES => [
+            Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1,
+            Config::XML_PATH_ALGORITHM => Calculation::CALC_UNIT_BASE,
+        ],
+        SetupUtil::TAX_RATE_OVERRIDES => [
+            SetupUtil::TAX_RATE_TX => 7.5,
+            SetupUtil::TAX_RATE_AUSTIN => 5.5,
+        ],
+        SetupUtil::TAX_RULE_OVERRIDES => [
+            [
+                //tax rule 1 for product
+                'code' => 'Product Tax Rule TX',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_TX],
+                'priority' => 1,
+            ],
+            [
+                //tax rule 2 for product
+                'code' => 'Product Tax Rule AUSTIN',
+                'tax_product_class' => [SetupUtil::PRODUCT_TAX_CLASS_1],
+                'tax_rate' => [SetupUtil::TAX_RATE_AUSTIN],
+                'priority' => 2,
+                'calculate_subtotal' => 1,
+            ],
+        ],
+    ],
+    'quote_data' => [
+        'billing_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+        ],
+        'shipping_address' => [
+            'region_id' => SetupUtil::REGION_TX,
+            'tax_postcode' => SetupUtil::AUSTIN_POST_CODE,
+        ],
+        'items' => [
+            [
+                'sku' => 'simple1',
+                'price' => 1,
+                'qty' => 10,
+            ],
+        ],
+    ],
+    'expected_results' => [
+        'address_data' => [
+            'subtotal' => 10,
+            'base_subtotal' => 10,
+            'subtotal_incl_tax' => 11.4,
+            'base_subtotal_incl_tax' => 11.4,
+            'tax_amount' => 1.4,
+            'base_tax_amount' => 1.4,
+            'shipping_amount' => 0,
+            'base_shipping_amount' => 0,
+            'shipping_incl_tax' => 0,
+            'base_shipping_incl_tax' => 0,
+            'shipping_taxable' => 0,
+            'base_shipping_taxable' => 0,
+            'shipping_tax_amount' => 0,
+            'base_shipping_tax_amount' => 0,
+            'discount_amount' => 0,
+            'base_discount_amount' => 0,
+            'hidden_tax_amount' => 0,
+            'base_hidden_tax_amount' => 0,
+            'shipping_hidden_tax_amount' => 0,
+            'base_shipping_hidden_tax_amount' => 0,
+            'grand_total' => 11.4,
+            'base_grand_total' => 11.4,
+            'applied_taxes' => [
+                SetupUtil::TAX_RATE_TX => [
+                    'percent' => 7.5,
+                    'amount' => 0.8,
+                    'base_amount' => 0.8,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_TX,
+                            'title' => SetupUtil::TAX_RATE_TX,
+                            'percent' => 7.5,
+                        ],
+                    ],
+                ],
+                SetupUtil::TAX_RATE_AUSTIN => [
+                    'percent' => 5.5,
+                    'amount' => 0.6,
+                    'base_amount' => 0.6,
+                    'rates' => [
+                        [
+                            'code' => SetupUtil::TAX_RATE_AUSTIN,
+                            'title' => SetupUtil::TAX_RATE_AUSTIN,
+                            'percent' => 5.5,
+                        ],
+                    ],
+                ],
+            ],
+        ],
+        'items_data' => [
+            'simple1' => [
+                'row_total' => 10,
+                'base_row_total' => 10,
+                'taxable_amount' => 1,
+                'base_taxable_amount' => 1,
+                'tax_percent' => 13,
+                'price' => 1,
+                'base_price' => 1,
+                'price_incl_tax' => 1.14,
+                'base_price_incl_tax' => 1.14,
+                'row_total_incl_tax' => 11.4,
+                'base_row_total_incl_tax' => 11.4,
+                'tax_amount' => 1.4,
+                'base_tax_amount' => 1.4,
+                'discount_amount' => 0,
+                'base_discount_amount' => 0,
+                'discount_percent' => 0,
+                'hidden_tax_amount' => 0,
+                'base_hidden_tax_amount' => 0,
+            ],
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
index 13097905b48..b98287caecc 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
@@ -40,4 +40,9 @@ require_once __DIR__ . '/scenarios/including_tax_total.php';
 require_once __DIR__ . '/scenarios/excluding_tax_multi_item_unit.php';
 require_once __DIR__ . '/scenarios/excluding_tax_multi_item_row.php';
 require_once __DIR__ . '/scenarios/excluding_tax_multi_item_total.php';
-
+require_once __DIR__ . '/scenarios/including_tax_cross_border_trade_disabled.php';
+require_once __DIR__ . '/scenarios/including_tax_cross_border_trade_enabled.php';
+require_once __DIR__ . '/scenarios/multi_tax_rule_total_calculate_subtotal_no.php';
+require_once __DIR__ . '/scenarios/multi_tax_rule_unit_calculate_subtotal_no.php';
+require_once __DIR__ . '/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php';
+require_once __DIR__ . '/scenarios/multi_tax_rule_unit_calculate_subtotal_yes.php';
diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/InstanceTest.php b/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/InstanceTest.php
index aa286b773b3..093a31ffad9 100644
--- a/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/InstanceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/InstanceTest.php
@@ -61,11 +61,6 @@ class InstanceTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetWidgetConfigAsArray()
     {
-        $this->markTestIncomplete(
-            'Functionality is failed because widget' .
-            ' "app/design/frontend/Magento/iphone_html5/etc/widget.xml" replaces' .
-            ' "new_products" widget in Catalog module'
-        );
         $config = $this->_model->setType('Magento\Catalog\Block\Product\Widget\NewWidget')->getWidgetConfigAsArray();
         $this->assertTrue(is_array($config));
         $element = null;
@@ -96,11 +91,6 @@ class InstanceTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetWidgetSupportedContainers()
     {
-        $this->markTestIncomplete(
-            'Functionality is failed because widget' .
-            ' "app/design/frontend/Magento/iphone_html5/etc/widget.xml" replaces' .
-            ' "new_products" widget in Catalog module'
-        );
         $this->_model->setType('Magento\Catalog\Block\Product\Widget\NewWidget');
         $containers = $this->_model->getWidgetSupportedContainers();
         $this->assertInternalType('array', $containers);
diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/WidgetTest.php b/dev/tests/integration/testsuite/Magento/Widget/Model/WidgetTest.php
index c5b4d41fd53..a6f8cbcc0fb 100644
--- a/dev/tests/integration/testsuite/Magento/Widget/Model/WidgetTest.php
+++ b/dev/tests/integration/testsuite/Magento/Widget/Model/WidgetTest.php
@@ -60,30 +60,14 @@ class WidgetTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetPlaceholderImageUrl($type, $expectedFile)
     {
-        $this->markTestIncomplete(
-            'Functionality is failed because widget' .
-            ' "app/design/frontend/Magento/iphone_html5/etc/widget.xml" replaces' .
-            ' "new_products" widget in Catalog module'
-        );
         $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
         \Magento\TestFramework\Helper\Bootstrap::getInstance()
             ->loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE);
         $objectManager->get('Magento\Framework\View\DesignInterface')->setDesignTheme('Magento/backend');
         $expectedFilePath = "/adminhtml/Magento/backend/en_US/{$expectedFile}";
-        $expectedPubFile = $objectManager->get(
-            'Magento\Framework\App\Filesystem'
-        )->getPath(
-            \Magento\Framework\App\Filesystem::STATIC_VIEW_DIR
-        ) . $expectedFilePath;
-
-        if (file_exists($expectedPubFile)) {
-            unlink($expectedPubFile);
-        }
 
         $url = $this->_model->getPlaceholderImageUrl($type);
-        $this->assertStringEndsWith($expectedFile, $url);
-        $this->assertFileExists($expectedPubFile);
-        return $expectedPubFile;
+        $this->assertStringEndsWith($expectedFilePath, $url);
     }
 
     /**
@@ -99,32 +83,4 @@ class WidgetTest extends \PHPUnit_Framework_TestCase
             'default image' => array('non_existing_widget_type', 'Magento_Widget/placeholder.gif')
         );
     }
-
-    /**
-     * Tests, that theme file is found anywhere in theme folders, not only in module directory.
-     *
-     * @magentoDataFixture Magento/Widget/_files/themes.php
-     * @magentoAppIsolation enabled
-     */
-    public function testGetPlaceholderImageUrlAtTheme()
-    {
-        \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(
-            array(
-                \Magento\Framework\App\Filesystem::PARAM_APP_DIRS => array(
-                    \Magento\Framework\App\Filesystem::THEMES_DIR => array(
-                        'path' => dirname(__DIR__) . '/_files/design'
-                    )
-                )
-            )
-        );
-        $actualFile = $this->testGetPlaceholderImageUrl(
-            'Magento\Catalog\Block\Product\Widget\NewWidget',
-            'Magento_Catalog/images/product_widget_new.gif'
-        );
-
-        $expectedFile = dirname(
-            __DIR__
-        ) . '/_files/design/adminhtml/Magento/backend/Magento_Catalog/web/images/product_widget_new.gif';
-        $this->assertFileEquals($expectedFile, $actualFile);
-    }
 }
diff --git a/dev/tests/integration/testsuite/Magento/Widget/_files/themes.php b/dev/tests/integration/testsuite/Magento/Widget/_files/themes.php
deleted file mode 100644
index c8910402548..00000000000
--- a/dev/tests/integration/testsuite/Magento/Widget/_files/themes.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-/**
- * Magento
- *
- * NOTICE OF LICENSE
- *
- * This source file is subject to the Open Software License (OSL 3.0)
- * that is bundled with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://opensource.org/licenses/osl-3.0.php
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to license@magentocommerce.com so we can send you a copy immediately.
- *
- * DISCLAIMER
- *
- * Do not edit or add to this file if you wish to upgrade Magento to newer
- * versions in the future. If you wish to customize Magento for your
- * needs please refer to http://www.magentocommerce.com for more information.
- *
- * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
- * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
- */
-
-\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(array(
-    \Magento\Framework\App\Filesystem::PARAM_APP_DIRS => array(
-        \Magento\Framework\App\Filesystem::THEMES_DIR => array('path' => __DIR__ . '/design')
-    )
-));
-
-\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\App\AreaList')
-    ->getArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE)
-    ->load(\Magento\Framework\App\Area::PART_CONFIG);
-/** @var $registration \Magento\Core\Model\Theme\Registration */
-$registration = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
-    'Magento\Core\Model\Theme\Registration'
-);
-$registration->register('*/*/theme.xml');
diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php b/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php
new file mode 100644
index 00000000000..e7fb311d7c2
--- /dev/null
+++ b/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\TestFramework\Utility;
+
+/**
+ * A helper to gather various changed files
+ * if INCREMENTAL_BUILD env variable is set by CI build infrastructure, only files changed in the
+ * branch are gathered, otherwise all files
+ */
+class ChangedFiles
+{
+    /**
+     * Returns array of PHP-files, that use or declare Magento application classes and Magento libs
+     *
+     * @param string $changedFilesList
+     * @return array
+     */
+    public static function getPhpFiles($changedFilesList)
+    {
+        $fileHelper = \Magento\TestFramework\Utility\Files::init();
+        $allPhpFiles = $fileHelper->getPhpFiles();
+        if (isset($_ENV['INCREMENTAL_BUILD'])) {
+            $phpFiles = file($changedFilesList, FILE_IGNORE_NEW_LINES);
+            foreach ($phpFiles as $key => $phpFile) {
+                $phpFiles[$key] = $fileHelper->getPathToSource() . '/' . $phpFile;
+            }
+            $phpFiles = \Magento\TestFramework\Utility\Files::composeDataSets($phpFiles);
+            $phpFiles = array_intersect_key($phpFiles, $allPhpFiles);
+        } else {
+            $phpFiles = $allPhpFiles;
+        }
+
+        return $phpFiles;
+    }
+}
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php
index 7cf7a9155ef..18fcb1f839c 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php
@@ -148,7 +148,7 @@ class ObsoleteCodeTest extends \PHPUnit_Framework_TestCase
                 $this->_testObsoleteConstants($content);
                 $this->_testObsoletePropertySkipCalculate($content);
             },
-            \Magento\TestFramework\Utility\Files::init()->getPhpFiles()
+            \Magento\TestFramework\Utility\ChangedFiles::getPhpFiles(__DIR__ . '/_files/changed_files.txt')
         );
     }
 
diff --git a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Adjustment/CalculatorTest.php b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Adjustment/CalculatorTest.php
index 5f7cbfb1931..8c8e61289a2 100644
--- a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Adjustment/CalculatorTest.php
+++ b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Adjustment/CalculatorTest.php
@@ -61,6 +61,11 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
      */
     protected $selectionFactory;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $taxData;
+
     /**
      * @var Calculator
      */
@@ -69,7 +74,7 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
     protected function setUp()
     {
         $this->saleableItem = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getPriceInfo', 'getPriceType', '__wakeup'])
+            ->setMethods(['getPriceInfo', 'getPriceType', '__wakeup', 'getStore'])
             ->disableOriginalConstructor()
             ->getMock();
         $priceInfo = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false);
@@ -81,6 +86,12 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
         }));
         $this->saleableItem->expects($this->any())->method('getPriceInfo')->will($this->returnValue($priceInfo));
 
+        $store = $this->getMockBuilder('Magento\Store\Model\Store')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $store->expects($this->any())->method('roundPrice')->will($this->returnArgument(0));
+
+        $this->saleableItem->expects($this->any())->method('getStore')->will($this->returnValue($store));
 
         $this->baseCalculator = $this->getMock('Magento\Framework\Pricing\Adjustment\Calculator', [], [], '', false);
         $this->amountFactory = $this->getMock('Magento\Framework\Pricing\Amount\AmountFactory', [], [], '', false);
@@ -99,7 +110,17 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
             return $bundlePrice;
         });
         $this->selectionFactory->expects($this->any())->method('create')->will($factoryCallback);
-        $this->model = new Calculator($this->baseCalculator, $this->amountFactory, $this->selectionFactory);
+
+        $this->taxData = $this->getMockBuilder('Magento\Tax\Helper\Data')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->model = new Calculator(
+            $this->baseCalculator,
+            $this->amountFactory,
+            $this->selectionFactory,
+            $this->taxData
+        );
     }
 
     protected function tearDown()
diff --git a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/BundleOptionPriceTest.php b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/BundleOptionPriceTest.php
index b0c0d288b64..629920fd72b 100644
--- a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/BundleOptionPriceTest.php
+++ b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/BundleOptionPriceTest.php
@@ -76,10 +76,20 @@ class BundleOptionPriceTest extends \PHPUnit_Framework_TestCase
             ->method('getPriceInfo')
             ->will($this->returnValue($this->priceInfoMock));
 
+        $store = $this->getMockBuilder('Magento\Store\Model\Store')
+            ->setMethods(['roundPrice', '__wakeup'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $store->expects($this->any())->method('roundPrice')->will($this->returnArgument(0));
+
         $this->saleableItemMock->expects($this->once())
             ->method('setQty')
             ->will($this->returnSelf());
 
+        $this->saleableItemMock->expects($this->any())
+            ->method('getStore')
+            ->will($this->returnValue($store));
+
         $this->selectionFactoryMock = $this->getMockBuilder('Magento\Bundle\Pricing\Price\BundleSelectionFactory')
             ->disableOriginalConstructor()
             ->getMock();
@@ -91,8 +101,13 @@ class BundleOptionPriceTest extends \PHPUnit_Framework_TestCase
         );
         $this->amountFactory->expects($this->any())->method('create')->will($factoryCallback);
         $this->baseCalculator = $this->getMock('Magento\Framework\Pricing\Adjustment\Calculator', [], [], '', false);
+
+        $taxData = $this->getMockBuilder('Magento\Tax\Helper\Data')
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $this->bundleCalculatorMock = $this->getMockBuilder('Magento\Bundle\Pricing\Adjustment\Calculator')
-            ->setConstructorArgs([$this->baseCalculator, $this->amountFactory, $this->selectionFactoryMock])
+            ->setConstructorArgs([$this->baseCalculator, $this->amountFactory, $this->selectionFactoryMock, $taxData])
             ->setMethods(['getOptionsAmount'])
             ->getMock();
         $this->objectManagerHelper = new ObjectManagerHelper($this);
diff --git a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/GroupPriceTest.php b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/GroupPriceTest.php
index 0ec45ccb35d..6039a1bf000 100644
--- a/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/GroupPriceTest.php
+++ b/dev/tests/unit/testsuite/Magento/Bundle/Pricing/Price/GroupPriceTest.php
@@ -26,88 +26,178 @@ namespace Magento\Bundle\Pricing\Price;
 class GroupPriceTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var GroupPrice
+     * @var \Magento\Bundle\Pricing\Price\GroupPrice
      */
-    protected $model;
+    protected $groupPrice;
 
     /**
-     * @var \Magento\Framework\Pricing\Object\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $saleable;
+    protected $productMock;
 
     /**
-     * @var \Magento\Framework\Pricing\PriceInfo\Base |\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Resource\Product|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $priceInfo;
+    protected $productResourceMock;
 
-    public function setUp()
-    {
-        $this->saleable = $this->getMockBuilder('Magento\Catalog\Model\Product')
-            ->setMethods(['getPriceInfo', 'getCustomerGroupId', 'getData', '__wakeup'])
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $this->priceInfo = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false);
-
-        $this->saleable->expects($this->once())
-            ->method('getPriceInfo')
-            ->will($this->returnValue($this->priceInfo));
+    /**
+     * @var \Magento\Framework\Pricing\Adjustment\Calculator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $calculatorMock;
 
-        $objectHelper = new \Magento\TestFramework\Helper\ObjectManager($this);
-        $this->model = $objectHelper->getObject(
-            'Magento\Bundle\Pricing\Price\GroupPrice',
-            [
-                'saleableItem' => $this->saleable
-            ]
-        );
-    }
+    /**
+     * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $customerSessionMock;
 
     /**
-     * @param $regularPrice
-     * @param $storedGroupPrice
-     * @param $value
-     * @param $percent
-     * @dataProvider getValueDataProvider
+     * @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject
      */
-    public function testGetValue($regularPrice, $storedGroupPrice, $value, $percent)
-    {
-        $customerGroupId = 234;
+    protected $customerMock;
 
-        $this->saleable->expects($this->atLeastOnce())
-            ->method('getCustomerGroupId')
-            ->will($this->returnValue($customerGroupId));
+    /**
+     * @var \Magento\Catalog\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeMock;
 
-        $this->saleable->expects($this->once())
-            ->method('getData')
-            ->with('group_price')
-            ->will($this->returnValue($storedGroupPrice));
+    /**
+     * @var \Magento\Catalog\Model\Product\Attribute\Backend\Groupprice|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $backendMock;
 
-        if (!empty($storedGroupPrice)) {
-            $price = $this->getMock('Magento\Framework\Pricing\Price\PriceInterface');
-            $this->priceInfo->expects($this->once())
-                ->method('getPrice')
-                ->with(\Magento\Catalog\Pricing\Price\RegularPrice::PRICE_CODE)
-                ->will($this->returnValue($price));
-            $price->expects($this->once())
-                ->method('getValue')
-                ->will($this->returnValue($regularPrice));
-        }
-        $this->assertEquals($value, $this->model->getValue());
-        $this->assertEquals($percent, $this->model->getDiscountPercent());
-    }
+    /**
+     * @var \Magento\Framework\Pricing\PriceInfo\Base|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceInfoMock;
 
     /**
-     * @return array
+     * @var \Magento\Catalog\Pricing\Price\RegularPrice|\PHPUnit_Framework_MockObject_MockObject
      */
-    public function getValueDataProvider()
+    protected $regularPrice;
+    /**
+     * Set up test case
+     */
+    public function setUp()
     {
-        return array(
-            ['regularPrice' => 100, 'storedGroupPrice'
-                => [['cust_group' => 234, 'website_price' => 40]], 'value' => 60, 'percent' => 60],
-            ['regularPrice' => 75, 'storedGroupPrice'
-                => [['cust_group' => 234, 'website_price' => 40]], 'value' => 45, 'percent' => 60],
-            ['regularPrice' => 75, 'storedGroupPrice'
-                => [], 'value' => false, 'percent' => null],
+        $this->productMock = $this->getMock(
+            'Magento\Catalog\Model\Product',
+            ['__wakeup', 'getCustomerGroupId', 'getPriceInfo', 'getResource', 'getData'],
+            [],
+            '',
+            false
+        );
+        $this->productResourceMock = $this->getMock(
+            'Magento\Catalog\Model\Resource\Product',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->calculatorMock = $this->getMock(
+            'Magento\Framework\Pricing\Adjustment\Calculator',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->customerSessionMock = $this->getMock(
+            'Magento\Customer\Model\Session',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->customerMock = $this->getMock(
+            'Magento\Customer\Model\Customer',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->attributeMock = $this->getMock(
+            'Magento\Catalog\Model\Entity\Attribute',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->backendMock = $this->getMock(
+            'Magento\Catalog\Model\Product\Attribute\Backend\Groupprice',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->priceInfoMock = $this->getMock(
+            'Magento\Framework\Pricing\PriceInfo\Base',
+            ['getPrice'],
+            [],
+            '',
+            false
+        );
+        $this->regularPrice = $this->getMock(
+            'Magento\Catalog\Pricing\Price\RegularPrice',
+            [],
+            [],
+            '',
+            false
         );
+        $this->productMock->expects($this->once())
+            ->method('getPriceInfo')
+            ->will($this->returnValue($this->priceInfoMock));
+
+        $this->groupPrice = new \Magento\Bundle\Pricing\Price\GroupPrice(
+            $this->productMock,
+            1,
+            $this->calculatorMock,
+            $this->customerSessionMock
+        );
+    }
+
+    public function testGetValue()
+    {
+        $this->priceInfoMock->expects($this->once())
+            ->method('getPrice')
+            ->with($this->equalTo('regular_price'))
+            ->will($this->returnValue($this->regularPrice));
+        $this->regularPrice->expects($this->once())
+            ->method('getValue')
+            ->will($this->returnValue(100));
+        $this->productMock->expects($this->once())
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(null));
+        $this->customerSessionMock->expects($this->once())
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(3));
+        $this->productMock->expects($this->once())
+            ->method('getResource')
+            ->will($this->returnValue($this->productResourceMock));
+        $this->productResourceMock->expects($this->once())
+            ->method('getAttribute')
+            ->with($this->equalTo('group_price'))
+            ->will($this->returnValue($this->attributeMock));
+        $this->attributeMock->expects($this->once())
+            ->method('getBackend')
+            ->will($this->returnValue($this->backendMock));
+        $this->backendMock->expects($this->once())
+            ->method('afterLoad')
+            ->with($this->equalTo($this->productMock))
+            ->will($this->returnValue($this->backendMock));
+        $this->productMock->expects($this->once())
+            ->method('getData')
+            ->with(
+                $this->equalTo('group_price'),
+                $this->equalTo(null)
+            )
+            ->will($this->returnValue(
+                [
+                    [
+                        'cust_group' => 3,
+                        'website_price' => 80
+                    ]
+                ]
+
+            ));
+        $this->assertEquals(20, $this->groupPrice->getValue());
     }
 }
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Model/Product/PriceModifierTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Model/Product/PriceModifierTest.php
new file mode 100644
index 00000000000..90db7848043
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Model/Product/PriceModifierTest.php
@@ -0,0 +1,161 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+namespace Magento\Catalog\Model\Product;
+
+class PriceModifierTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\Product\PriceModifier
+     */
+    protected $priceModifier;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var array
+     */
+    protected $prices = array();
+
+    protected function setUp()
+    {
+        $this->productMock =
+            $this->getMock('Magento\Catalog\Model\Product',
+                array('getData', 'setData', '__wakeup'), array(), '', false);
+        $this->priceModifier = new \Magento\Catalog\Model\Product\PriceModifier();
+        $this->prices = array(
+            0 => array(
+                'all_groups' => 0,
+                'cust_group' => 1,
+                'price_qty' => 15,
+                'website_id' => 1
+            ),
+            1 => array(
+                'all_groups' => 1,
+                'cust_group' => 0,
+                'price_qty' => 10,
+                'website_id' => 1
+            )
+        );
+    }
+
+    public function testSuccessfullyRemoveGroupPriceSpecifiedForOneGroup()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue($this->prices));
+        $expectedPrices = array(1 => $this->prices[1]);
+        $this->productMock->expects($this->once())->method('setData')->with('group_price', $expectedPrices);
+        $this->priceModifier->removeGroupPrice($this->productMock, 1, 1);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedMessage This product doesn't have group price
+     */
+    public function testRemoveWhenGroupPricesNotExists()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array()));
+        $this->productMock->expects($this->never())->method('setData');
+        $this->priceModifier->removeGroupPrice($this->productMock, 1, 1);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedMessage For current  customerGroupId = '10' any group price exist'.
+     */
+    public function testRemoveGroupPriceForNonExistingCustomerGroup()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue($this->prices));
+        $this->productMock->expects($this->never())->method('setData');
+        $this->priceModifier->removeGroupPrice($this->productMock, 10, 1);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedMessage This product doesn't have tier price
+     */
+    public function testRemoveWhenTierPricesNotExists()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(array()));
+        $this->productMock->expects($this->never())->method('setData');
+        $this->priceModifier->removeTierPrice($this->productMock, 1, 3, 1);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedMessage For current  customerGroupId = '10' with 'qty' = 15 any tier price exist'.
+     */
+    public function testRemoveTierPriceForNonExistingCustomerGroup()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue($this->prices));
+        $this->productMock->expects($this->never())->method('setData');
+        $this->priceModifier->removeTierPrice($this->productMock, 10, 15, 1);
+    }
+
+    public function testSuccessfullyRemoveTierPriceSpecifiedForAllGroups()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue($this->prices));
+        $expectedPrices = array($this->prices[0]);
+        $this->productMock->expects($this->once())->method('setData')->with('tier_price', $expectedPrices);
+        $this->priceModifier->removeTierPrice($this->productMock, 'all', 10, 1);
+    }
+
+    public function testSuccessfullyRemoveTierPriceSpecifiedForSpecificGroups()
+    {
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue($this->prices));
+        $expectedPrices = array(1 => $this->prices[1]);
+        $this->productMock->expects($this->once())->method('setData')->with('tier_price', $expectedPrices);
+        $this->priceModifier->removeTierPrice($this->productMock, 1, 15, 1);
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
new file mode 100644
index 00000000000..b73e577eff0
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Model;
+
+class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductRepository
+     */
+    protected $model;
+
+    protected function setUp()
+    {
+        $productFactoryMock = $this->getMock(
+            'Magento\Catalog\Model\ProductFactory', array('create'), array(), '', false
+        );
+        $this->productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $productFactoryMock->expects($this->once())->method('create')->will($this->returnValue($this->productMock));
+        $this->model = new ProductRepository($productFactoryMock);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testCreateThrowsExceptionIfNoSuchProduct()
+    {
+        $this->productMock->expects($this->once())->method('getIdBySku')->with('test_sku')
+            ->will($this->returnValue(null));
+        $this->model->get('test_sku');
+    }
+
+    public function testCreateCreatesProduct()
+    {
+        $this->productMock->expects($this->once())->method('getIdBySku')->with('test_sku')
+            ->will($this->returnValue('test_id'));
+        $this->productMock->expects($this->once())->method('load')->with('test_id');
+        $this->assertSame($this->productMock, $this->model->get('test_sku'));
+        $this->assertSame($this->productMock, $this->model->get('test_sku'));
+    }
+
+    public function testCreateCreatesProductInEditMode()
+    {
+        $this->productMock->expects($this->once())->method('getIdBySku')->with('test_sku')
+            ->will($this->returnValue('test_id'));
+        $this->productMock->expects($this->once())->method('setData')->with('_edit_mode', true);
+        $this->productMock->expects($this->once())->method('load')->with('test_id');
+        $this->assertSame($this->productMock, $this->model->get('test_sku', true));
+    }
+} 
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Pricing/Price/GroupPriceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Pricing/Price/GroupPriceTest.php
index 36d889fbdeb..395a35555c2 100644
--- a/dev/tests/unit/testsuite/Magento/Catalog/Pricing/Price/GroupPriceTest.php
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Pricing/Price/GroupPriceTest.php
@@ -30,211 +30,206 @@ namespace Magento\Catalog\Pricing\Price;
 class GroupPriceTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\TestFramework\Helper\ObjectManager
+     * @var \Magento\Catalog\Pricing\Price\GroupPrice
      */
-    protected $objectManager;
-
-    public function setUp()
-    {
-        $this->objectManager = new \Magento\TestFramework\Helper\ObjectManager($this);
-    }
+    protected $groupPrice;
 
     /**
-     * @param array|null $groupPrice
-     * @param int $customerGroup
-     * @param float $expected
-     *
-     * @dataProvider groupPriceDataProvider
+     * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject
      */
-    public function testGroupPrice($groupPrice, $customerGroup, $expected)
-    {
-        $saleableItemMock = $this->prepareSaleableItem($groupPrice);
-        $sessionMock = $this->prepareSession($saleableItemMock, $customerGroup);
-        $groupPriceModel = $this->objectManager->getObject(
-            'Magento\Catalog\Pricing\Price\GroupPrice',
-            [
-                'saleableItem'     => $saleableItemMock,
-                'customerSession' => $sessionMock
-            ]
-        );
-        $this->assertEquals($expected, $groupPriceModel->getValue());
-    }
+    protected $productMock;
 
     /**
-     * @param \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Product $saleableItemMock
-     * @param int $customerGroup
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\Session
+     * @var \Magento\Catalog\Model\Resource\Product|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected function prepareSession($saleableItemMock, $customerGroup)
-    {
-        $session = $this->getMock('Magento\Customer\Model\Session', ['getCustomerGroupId'], [], '', false);
-        $session->expects($this->any())
-            ->method('getCustomerGroupId')
-            ->will($this->returnValue($customerGroup));
+    protected $productResourceMock;
 
-        $saleableItemMock->expects($this->any())
-            ->method('getCustomerGroupId')
-            ->will($this->returnValue(false));
+    /**
+     * @var \Magento\Framework\Pricing\Adjustment\Calculator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $calculatorMock;
 
-        return $session;
-    }
+    /**
+     * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $customerSessionMock;
 
     /**
-     * @dataProvider groupPriceNonExistDataProvider
-     *
-     * @param array|null $groupPrice
-     * @param float $expected
+     * @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject
      */
-    public function testGroupPriceNonExist($groupPrice, $expected)
-    {
-        $groupPriceModel = $this->objectManager->getObject(
-            'Magento\Catalog\Pricing\Price\GroupPrice',
-            [
-                'saleableItem'     => $this->prepareSaleableItem($groupPrice),
-                'customerSession' => $this->getMock('Magento\Customer\Model\Session', [], [], '', false)
-            ]
-        );
+    protected $customerMock;
 
-        $this->assertEquals($expected, $groupPriceModel->getValue());
+    /**
+     * @var \Magento\Catalog\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeMock;
 
-        //Verify that storedGroupPrice is cached
-        $this->assertEquals($expected, $groupPriceModel->getValue());
-    }
+    /**
+     * @var \Magento\Catalog\Model\Product\Attribute\Backend\Groupprice|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $backendMock;
 
     /**
-     * @param array|null $groupPrice
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * Set up test case
      */
-    protected function prepareSaleableItem($groupPrice)
+    public function setUp()
     {
-        $saleableItemMock = $this->getMock(
+        $this->productMock = $this->getMock(
             'Magento\Catalog\Model\Product',
-            ['getCustomerGroupId', 'getData', 'getPrice', 'getPriceInfo', 'getResource', '__wakeup'],
+            ['__wakeup', 'getCustomerGroupId', 'getPriceInfo', 'getResource', 'getData'],
+            [],
+            '',
+            false
+        );
+        $this->productResourceMock = $this->getMock(
+            'Magento\Catalog\Model\Resource\Product',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->calculatorMock = $this->getMock(
+            'Magento\Framework\Pricing\Adjustment\Calculator',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->customerSessionMock = $this->getMock(
+            'Magento\Customer\Model\Session',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->customerMock = $this->getMock(
+            'Magento\Customer\Model\Customer',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->attributeMock = $this->getMock(
+            'Magento\Catalog\Model\Entity\Attribute',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->backendMock = $this->getMock(
+            'Magento\Catalog\Model\Product\Attribute\Backend\Groupprice',
+            [],
             [],
             '',
             false
         );
 
-        $saleableItemMock->expects($this->at(1))
-            ->method('getData')
-            ->will($this->returnValue(null));
-
-        $saleableItemMock->expects($this->at(2))
-            ->method('getData')
-            ->will($this->returnValue($groupPrice));
-
-        $saleableItemMock->expects($this->any())
-            ->method('getResource')
-            ->will($this->returnValue($this->prepareSaleableItemResource()));
-
-        $priceInfo = $this->getMockBuilder(
-            'Magento\Framework\Pricing\PriceInfo\Base'
-        )->disableOriginalConstructor()->getMockForAbstractClass();
-
-        $priceInfo->expects($this->any())
-            ->method('getAdjustments')
-            ->will($this->returnValue([]));
-
-        $saleableItemMock->expects($this->any())
-            ->method('getPriceInfo')
-            ->will($this->returnValue($priceInfo));
-
-        return $saleableItemMock;
+        $this->groupPrice = new \Magento\Catalog\Pricing\Price\GroupPrice(
+            $this->productMock,
+            1,
+            $this->calculatorMock,
+            $this->customerSessionMock
+        );
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Resource\Product
+     * test get group price, customer group in session
      */
-    protected function prepareSaleableItemResource()
+    public function testGroupPriceCustomerGroupInSession()
     {
-        $resourceMock = $this->getMockBuilder(
-            'Magento\Catalog\Model\Resource\Product'
-        )->disableOriginalConstructor()->setMethods(['getAttribute', '__wakeup'])->getMock();
-
-        $attributeMock = $this->getMock(
-            'Magento\Framework\Object',
-            ['getBackend', 'afterLoad'],
-            [],
-            '',
-            false
-        );
-
-        $attributeMock->expects($this->any())
+        $this->productMock->expects($this->once())
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(null));
+        $this->customerSessionMock->expects($this->once())
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(3));
+        $this->productMock->expects($this->once())
+            ->method('getResource')
+            ->will($this->returnValue($this->productResourceMock));
+        $this->productResourceMock->expects($this->once())
+            ->method('getAttribute')
+            ->with($this->equalTo('group_price'))
+            ->will($this->returnValue($this->attributeMock));
+        $this->attributeMock->expects($this->once())
             ->method('getBackend')
-            ->will($this->returnValue($attributeMock));
-
-        $attributeMock->expects($this->any())
+            ->will($this->returnValue($this->backendMock));
+        $this->backendMock->expects($this->once())
             ->method('afterLoad')
-            ->will($this->returnValue($attributeMock));
-
-        $resourceMock->expects($this->any())
-            ->method('getAttribute')
-            ->will($this->returnValue($attributeMock));
+            ->with($this->equalTo($this->productMock))
+            ->will($this->returnValue($this->backendMock));
+        $this->productMock->expects($this->once())
+            ->method('getData')
+            ->with(
+                $this->equalTo('group_price'),
+                $this->equalTo(null)
+            )
+            ->will($this->returnValue(
+                [
+                    [
+                        'cust_group' => 3,
+                        'website_price' => 80
+                    ]
+                ]
 
-        return $resourceMock;
+            ));
+        $this->assertEquals(80, $this->groupPrice->getValue());
     }
 
     /**
-     * @return array
+     * test get group price, customer group in session
      */
-    public function groupPriceDataProvider()
+    public function testGroupPriceCustomerGroupInProduct()
     {
-        return [
-            [
-                'groupPrice' => [
-                    [
-                        'cust_group'    => 1,
-                        'website_price' => 90.9
-                    ],
-                    [
-                        'cust_group'    => 2,
-                        'website_price' => 80.8
-                    ],
+        $this->productMock->expects($this->exactly(2))
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(3));
+        $this->productMock->expects($this->once())
+            ->method('getResource')
+            ->will($this->returnValue($this->productResourceMock));
+        $this->productResourceMock->expects($this->once())
+            ->method('getAttribute')
+            ->with($this->equalTo('group_price'))
+            ->will($this->returnValue($this->attributeMock));
+        $this->attributeMock->expects($this->once())
+            ->method('getBackend')
+            ->will($this->returnValue($this->backendMock));
+        $this->backendMock->expects($this->once())
+            ->method('afterLoad')
+            ->with($this->equalTo($this->productMock))
+            ->will($this->returnValue($this->backendMock));
+        $this->productMock->expects($this->once())
+            ->method('getData')
+            ->with(
+                $this->equalTo('group_price'),
+                $this->equalTo(null)
+            )
+            ->will($this->returnValue(
+                [
                     [
-                        'cust_group'    => 1,
-                        'website_price' => 70.7
+                        'cust_group' => 3,
+                        'website_price' => 80
                     ]
-                ],
-                'customer_group'   => 1,
-                'expected'         => 90.9
-            ],
-            [
-                'groupPrice' => [
-                    [
-                        'cust_group'    => 2,
-                        'website_price' => 10.1
-                    ],
-                    [
-                        'cust_group'    => 1,
-                        'website_price' => 20.2
-                    ],
-                ],
-                'customer_group'   => 1,
-                'expected'         => 20.2
-            ],
-            [
-                'groupPrice' => [
-                    [
-                        'cust_group'    => 1,
-                        'website_price' => 90.9
-                    ],
-                ],
-                'customer_group'   => 2,
-                'expected'         => false
-            ]
-        ];
+                ]
+
+            ));
+        $this->assertEquals(80, $this->groupPrice->getValue());
     }
 
     /**
-     * @return array
+     * test get group price, attribut is noy srt
      */
-    public function groupPriceNonExistDataProvider()
+    public function testGroupPriceAttributeIsNotSet()
     {
-        return [
-            [
-                'groupPrice'       => null,
-                'expected'         => false
-            ]
-        ];
+        $this->productMock->expects($this->exactly(2))
+            ->method('getCustomerGroupId')
+            ->will($this->returnValue(3));
+        $this->productMock->expects($this->once())
+            ->method('getResource')
+            ->will($this->returnValue($this->productResourceMock));
+        $this->productResourceMock->expects($this->once())
+            ->method('getAttribute')
+            ->with($this->equalTo('group_price'))
+            ->will($this->returnValue(null));
+        $this->assertFalse($this->groupPrice->getValue());
     }
 }
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidatorTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidatorTest.php
new file mode 100644
index 00000000000..4dc8ca67d7c
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/GalleryEntryContentValidatorTest.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media\Data;
+
+class GalleryEntryContentValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var GalleryEntryContentValidator
+     */
+    private $validator;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $entryContentMock;
+
+    /**
+     * @var string
+     */
+    private $testImagePath;
+
+    protected function setUp()
+    {
+        $this->validator = new GalleryEntryContentValidator();
+        $this->entryContentMock = $this->getMock(
+            '\Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContent',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->testImagePath = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'magento_image.jpg';
+    }
+
+    public function testIsValid()
+    {
+        $this->entryContentMock->expects($this->any())->method('getData')->will($this->returnValue(
+            base64_encode(file_get_contents($this->testImagePath))
+        ));
+        $this->entryContentMock->expects($this->any())->method('getName')->will($this->returnValue(
+            'valid_name'
+        ));
+        $this->entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            'image/jpeg'
+        ));
+        $this->assertTrue($this->validator->isValid($this->entryContentMock));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage The image content must be valid base64 encoded data.
+     */
+    public function testIsValidThrowsExceptionIfProvidedContentIsNotBase64Encoded()
+    {
+        $this->entryContentMock->expects($this->any())->method('getData')->will($this->returnValue(
+            'not_a_base64_encoded_content'
+        ));
+        $this->entryContentMock->expects($this->any())->method('getName')->will($this->returnValue(
+            'valid_name'
+        ));
+        $this->entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            'image/jpeg'
+        ));
+        $this->assertTrue($this->validator->isValid($this->entryContentMock));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage The image content must be valid base64 encoded data.
+     */
+    public function testIsValidThrowsExceptionIfProvidedContentIsNotAnImage()
+    {
+        $this->entryContentMock->expects($this->any())->method('getData')->will($this->returnValue(
+            base64_encode('not_an_image_data')
+        ));
+        $this->entryContentMock->expects($this->any())->method('getName')->will($this->returnValue(
+            'valid_name'
+        ));
+        $this->entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            'image/jpeg'
+        ));
+        $this->assertTrue($this->validator->isValid($this->entryContentMock));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage The image MIME type is not valid or not supported.
+     */
+    public function testIsValidThrowsExceptionIfProvidedImageHasWrongMimeType()
+    {
+        $this->entryContentMock->expects($this->any())->method('getData')->will($this->returnValue(
+            base64_encode(file_get_contents($this->testImagePath))
+        ));
+        $this->entryContentMock->expects($this->any())->method('getName')->will($this->returnValue(
+            'valid_name'
+        ));
+        $this->entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            'wrong_mime_type'
+        ));
+        $this->assertTrue($this->validator->isValid($this->entryContentMock));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Provided image name contains forbidden characters.
+     * @dataProvider getInvalidImageNames
+     * @param string $imageName
+     */
+    public function testIsValidThrowsExceptionIfProvidedImageNameContainsForbiddenCharacters($imageName)
+    {
+        $this->entryContentMock->expects($this->any())->method('getData')->will($this->returnValue(
+            base64_encode(file_get_contents($this->testImagePath))
+        ));
+        $this->entryContentMock->expects($this->any())->method('getName')->will($this->returnValue(
+            $imageName
+        ));
+        $this->entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            'image/jpeg'
+        ));
+        $this->assertTrue($this->validator->isValid($this->entryContentMock));
+    }
+
+    /**
+     * @return array
+     */
+    public function getInvalidImageNames()
+    {
+        return array(
+            array('test/test'),
+            array('test\test'),
+            array('test:test'),
+            array('test"test'),
+            array('test*test'),
+            array('test;test'),
+            array('test(test'),
+            array('test)test'),
+            array('test<test'),
+            array('test>test'),
+            array('test?test'),
+            array('test{test'),
+            array('test}test'),
+            array('test|test'),
+            array('test|test'),
+        );
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/_files/magento_image.jpg b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/Data/_files/magento_image.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..bdda86b647e7f0cae331f3bdff83b07cf3b3dce6
GIT binary patch
literal 13873
zcmd6Nbx>Siw`JoAmJlpKL$KiP7J|Dr?(PnaH3WhL5AF_)ySoSX#@*c=0u1?m-@I3O
zZ|40wv%Biv?sIP6I#p}0b+(-6+2>WjTS-v~Q2-1q0ATd;0X!c9Foc~AE!_Yx0C)fZ
zAoVhI3_uq&vNbRU7`4BQz&tMi`~YyUf9&N7hX4l$hkyVN2M3RUi15b{Um+sB7~-o}
zuaS|FU%x{A$I$=S+y5{aICywOL`2kAuTbBiqoTjV`ZI%t@y|TQziw~;efy99{PpFz
z9f0u)1_9<R91I2k76S$j1LnCK(EGCNmq8eqmxcb@UcIaf=`|eepFvo-zorn8;1OQE
z%)AA_z`?>H!n`aQ6&B^?e}A5YN5DWtB4H9#c=t-*zTy*R6;@;>^H)U!Qfwaw7DFSa
zoa&Bo->ga{M=~6Cj@Q|Kr%~U9mC4zF#uT_1FYkkaef<jQ6)X(GKg)o5c^?MCpSRgp
zymLT2)%WT6#9YZ5nK`bANlL!<)gX(-k&LYqE9%Tw$uRnP9)Jq>vIYz|41fS&OA+wx
z<15(zzb;GM9P`I6&>2D3CgB4LZP$J5$?(KNJke25sIU0_2u!^Vxd?*}iEq?2NA>Ui
zAl5P!W^cPwur@e~kDoy2d_x<0poL&k;gX)pdKRT~KB?<nA>4ik`QZ#yT4l12qVNfE
zz2YE18t8D-_LM-7Gm!0il*$C>wLEkHmBlNCE=Z+aK+%#WJF?LtR6bgMFn}*_;s56g
zqQXz7QEm%ssD=Z$<|@LR=u8oe?26~*RGBW)Dzo#AMpsi8frdTTIb}}uD}G4jZedxp
z0U}LR*^&%Rw2pzv9;I0#@u!EH9S)~$o<%+0T9s#jov+L@z!WnOA~xa0+oe%hG#XS$
zO=qMX;AK3~z|BW~<6y@}P97z9LAG=J>4HL={*t5(fCf|Y9;O_SnH%F()^7Hwc_c<+
zL6zUEj>mj;{YCqPb(rXYH~B0HqYATUXpdJ;F<v`GLP5q*cfrK?R}${RKFz|3@9sfK
zltBEaE!q9Dbz2R!h9eT2>^*w-iKh5^lLL>;u+3miKec`dd88``p8!tJ$?t)0ODs?Z
z(n^{XxEJ?gS@Xy~<4<oEHRa5u)>4R3;FS9B_YUbFn3?(h&u#`cA0Aw}0mMdYeNAm)
zYG;IXS}`F(I?_H}lMyBNv6bk$23xE46H(-^od!H85ZE2$uN<SnG3W0*T)uK;KAM@w
z$xj3S1~yBu+5HYb;){StYkY`gnXL}BC7m%l_DwwQSL4YL3A;sJ*gM*|y2|w1NxTz!
znb(1-uPx&$TXp)Ue^_zp_|jOzzrSxZ!=G4`z2Q7@jKF;-0W1*_j&9K<jUCY{)Q*X<
zQ|ylHgjy<na_Ak=R$)qbaSj;U)>+zyv>Co=#&Hq5i~>RyMJOX*_@*7tk5$p}4v#L@
zNIs$-W#xeYHW^kRMzKQ;!PKu{5jw-e>h6Bq`LFAzOim3h5NYke-3xJ-Cz{dodK#jG
z^(HH0)!&mP+>>KUxHkZ`5V|+d05)Bec%>GuS9y*#i^Vk;8Yftl<0?v=m059_;o)kF
zoLr><e?u`+BMYv=;E-5k12dzb{eHX>2Sbted?f~<?*D=9Xl2=t^*bdRaooFE>>=m^
z=5B?CjXF&8A5FSQ&fqzcV{9(l%y%b7E*_x=!(|Cci&|9bZq!_u1_IzYkkX084#jf3
znb(&I3-&sC`JhsC8*JR9j5+E}GQBS@0H(a%a+p!V{oG~-_oQCWp2LTNkfS|r^qsYR
znwmDM+9sMAl=#_{J}ZP=vP=LV3h+L<9MC2hzCH91fBp^3C-r!}iIi>S(}hDs)GCw3
zbmh|2ZmapK0Nbt&)_qv`YBx3EwA3@*d?Tg?mtlKFRpLkO;>tHH7J>I$_D@+#-#AY#
z%R%(HS9IhF>bv}U&2$|U=$`5NF<hmVAzCO}>~BTkiQm>hws6MU0AKXEW3wAjfca?j
zmUT5#bha{V6R4!c-{Ymf5r|E>(4A^ppiE?iP5(Olpiv*69ToX7zn<N!ZZzB#+3(Xo
z39ZdzdR-GyOM8|Z5w|DYtLyM!4_bB_Ppiid-~SOnUJoV-@f6*%TJjW>Q#DjA|2-K0
z^~#Xz{%H-lsRej+7wOTLpybAE-fiAP^-=%-qCOuGrTVzq<d26cHy?~<@B-hGIazmp
z7QCD2vopkBU&q1LwqhMxi0unH(0&~=w{&AHW0j7MND~iDQhnzX%^>=~KX6AirdxH8
zJ%k&lcdh8OVVmduU56>QEBPC-YH$&Q4rP*u`WyYkr}KvwwYji6<>Modg3gg8wr;rd
zU&mg)`%r9UjjD%c`324FJpc)iiLCJ|j=1Rb@fEUV?7*CH4hPDxmXq>=yHU)M8-BOd
z`-Yv4Od}Dv^bj0J9QQck1pDm)3xciU8I;iQ$}P^VoB%Dg$+!FU%GHN=Iroq>8GcnN
zG(x!4MJdMydlIzflOOBWRovZu&|fVdoN-IYj{^Dj!#o)p#s{d<RyPE3_&71cSq96A
zF`5Q)ovN)3i^JGg(m#2y765fXhm%>kZRor!Zu8(DDwc07)K{d7AW5U**OR_G$RA*w
z-oezpH+p}WPBDsbqbfdA5(VjcJPbLScGHeugg*8xL@a5UXR~L&>z{7}l@AT#m<4DQ
zy0&1-DDA3FvU)4u5D<;`MJ<aIQmKO)S}w;ndj=&ewLwrv^76*TLoKiH>UJ9%ne}n+
zoSO$iO&Suw2RQHtgI>Ribcw*hsz_=sT67a$_5K`e`#~Q@dV>nX4?Zw=-$>!A3D>;7
z3t3W{a|y_&@?B8H882L%ISyGpZ7DtjM!Ls$IUm{Gq_mU=_SYr$ij7ihy^O|Mr1ohn
zAAAcet)uWsqtBZpjV4k*=g^Nfnx{HV>>tS5)k+B;XU$T(OBLk7?TCE7)MDJUC-|aL
zLg<MtJs;H9Jf|dyMY!cOB`;CIgU!OQq>U2freTPpXWseW<(rSHpwQBSG$%XgVwA<t
zt_xFY`KEpqZ@TWC#EZ&0M!uVB51iB}RTh>4N{D1Hg_{gs{?uhtYwWQ`D6>vuo>7%u
zy5$Yke8p?u^PAD;>UDE{Y-Cm~`ee&t(9u**wT;f}IAgJ?(k?Lcggzal<Ey__92s}c
zmqmlFT-#-p8EC2JQ*Urg?-5S_BGjBwKz$|Nsy{-ln$o;mkdYfk>YqOi5#I85gO+(3
z*{Vqf3}ao;=~7N*Ry1q|pxPmfkbT1&Z;o;Sa7MLhOgYR3ZY)pdmTJCjcl0Wob+}{}
zGICPou7#UXJ{Q4)x0-kPAoug~Hg(Vvfv_-H!eU!U2<WCZHK!d1;b5ReY^mNF7MsQ&
zwAfpq_00$XZ}G6lof`9U0sw2}=wAwqI2<(%hR2`>56G>j>%yJ$Ry45(Az6#=s)$jR
zW=Mw0apELR+uL{`iSJu)NUj{yY$=J-1->cv(ch5?FB~%Q?;Ad_#iq)D%TzUMuYPT9
zU1PrMh^K8ZSRpL!l^l?CZHI{rWjeE8mJ3qk%rdf`vrr2NJLfvEjGN+Pz>xwz`D$9P
zC&nsI!p`)N3KD;hJkzZdovAbKw;&GnNUF`6%@La!=`C?finEMU3Yp=m?<aVF3GF_%
zZ`KbdeiJKZ(zHU@_NIF3*VLBZQio2mcwrO0wCq>(ZGy-GZUHwo#o7#HF`U{BI4O%y
z1e&aR?qAO4>OzziJzC0Y4B8viT2%HG`<ref6@Ni5#Z%3aCdW+fc+{=xX4TORot)U<
z-ErRw-bjR>$dtf$pKL!-2PrG475+%3AIi;^aLcN}Ry8jQ>JNN^bnlWByOIq3MCv6K
zcO;r@7<ROWC(^gj>SeQ1F8^66+CpbBAGm9A$6vyS*9l_C5!zDDZ8er6vaXRa9Ey3n
zm`{y}`Db?yjC_Ag>Vq$a5D#A|eCdI$phW#1TfU3elAuJ4&4CKTIi$kls^LP{$$xuC
zT*BiJ`wd#*58KWUIzmQfs{}%A?~9m^)=e5PYF-;W^oQg!JGlq*Kdc5asYtTT%h}sQ
z{0AokQ^j-b>SpFGO=_2@a<Ypc_TgD^F5Fy(9-x{bJ9vz~f=Qy8Pc$k}#eygfrh%DH
zSrAq&Q{M;P|3WpirA%<f0Z!AU)?w{4pyB+Cw2Vn>{<a{E^P%YrL&Dmnm>leCO^vFb
zykTd<&!sVK%qhBpIUDEw(V~=Hov`aLNF$*b!L)^33&Rp28&_~m<`g=a-WrNaEe?^)
zLg0X73~Xzqdu+v^AsE_o9k(>@GBHkGETwxw&mX!($exT?IV8%1<$M~=xi3WrtQhV!
z==2mF;X0xNVj0&Ts+AfCc#tuL&LtIpU}kT;<GKk#WL(JTxMDzZ43U5dyC3Q5!rmPK
zx?r~l#?%bkAg<{u<g(CbZkGs`a(VSiZclF1&PZ<T_fi670&_eW&)LNDk9W9g^Nd^1
zx5iVxFV|hlbN7}moU<{T4;L>cVb0lDjK*T<T+nFTX4{cWATdW_wjr{v>#U7>M@sJQ
zo~px|yPsc?0CnP<qZ{Yq5k)z0<!WTVnTH1%9kFBu#oe1gRay1+XR4jwAfgQu<Hab+
z_o5TnjI^l#h`ExBMkE`uq8l{OPtSkJi#lqE#m)nA<@*tqV=JaG{G7+D-8)uKa(I#3
z3$nwlXnY(Y0@Z^7xo7L0i9b-vz){=0sjPE>GyxS6WUW~^@q!E_tR5(G5x=srV=Tfd
zjcOVQ5+>vMVSz3>6JH<nG+8R_8EE(X#;$2kk}o^gZphcuvr>;QOQy&g<5cY#rOGRw
zq?y(UvSLA&NedpsBMFnT)*=PYXYfkg-P%EXQ{9a$2OKI2BU~QbaD-V(*p{LUHBg+|
zY`yL{{mL&~b++vOcwlM>&e~THwuiRQEH2d8P9fe-M@IkW9ZCsZ#YmUIfRQMC^sJ8A
zVfR{vNhPKxxkP})i6ysfDb9>o;x6C(;V&Eo6XY+X#eQqMfjp>WdiQU-9oml+k&z1u
z;>+t<j6UI(pwU@)b{`oz7|PlBa~+iNC0IQ9ck$g9CYYdBV32L2IBIRT580U)`rX@Y
z{8-pyAxw2GcqHE-xMkvDzQV>{bCi(^Y+l@(PGoaT!myV4JLZAEI2u&GfTV6pw@~Po
z(&sqSih3A}GEekZg}lC$n>I&K!VMEzFNrtyI!`5WRP1G&L+0aO*B_926gYnK$uwTf
zx)~$Bv{G-nL;2JkV!uZ403bWIJmm*Qq8TabfsDag4H~Xc?MV^;mYl15zO?U#Dsw))
z<LYvLoxwRhEM)9W*tJ<_9)-caz6pkWTF1L#%>g|9ym(BM-_+iGTM$tV7|Jn*+N@`w
zMXf{{fO&LIR0g{be`U!bn=$f{oznL#qv>EM6|Gcu&nwYqRaA)7vD9h~A_)kF?kxve
zP?Rmr8U<wG0Gmp+OQR}1WhcoJ(tf%g_4bIH@8b39AGNUyU+>ct_5N5P1(yG=^%z{%
z{!kNrAmqU^paB*`5Y-Fdf|#LWF}<lm|GiOH{|uP4h!X<Q#9J8GokAGu6m+CCqXZ;3
z$WqD4>Ckwn%+{?Kf591sg}p>>s{l=?uy6&MVqqyZU;N(fzw%rn#||`US>H@w@nsa9
zUVtEy4}ur9#;c2x)b!0Gk?b@NS`0;~IDxJhslET^Bj$zYpak2J!O_<ynq<mZ>rW*e
zMhR}`DNoR+<r*aM<0$5|H1oAQ{>slpFFV5?4<Ty({TI81TV+LvPboOglj8}vHKc}X
ziy?t&6}>H%enMH7_lC=PvUQzU65nc+QA(>Xg*0mY*X4}h8n0~EwEpPZ6Wq_6&_VoK
zBPm>dZ*#+m8Fts}HJ3?8V_6#m*)3J|Dz;MmW!<9SH6f18eSOYvt~Kqe+x7OA-$>jm
zQS(A;)U33SjW1Oc%AM9-t<(?wpi+u@?%zxAP~<}6_DvrjKqhpothTmAL0x{6)|TE_
z&OGh3ZiVY1u=={HIRkkaNmlfSb?L-kJ;c2i!^?K8MfyGj#Rs>!)CH?ct0YMqLTu?X
z!x>1EgHAhKwXYcBYI*FfDd6UBbAs_}tz){%>m7b#YvpnNU0IOUrh5j+o<9TjBi&+&
zJ?`c}F?+4VPql<l<|vr=gqaqvVX9bfbRC~WerU|`&|m*zdMfl?oB0@$>)$oiH-l7d
zb;r53iZhT@DLy@9o+ty<Ucu@s?LOR_Ntsbk=YL7cOm=rIEzpd=*j>pZ-Z$b=G#mQK
zS08Z_K8)gGlLW+0)~)x|vLqKVp*Ay^<>Rh)E){T{QN#{rzE)T`F*paxAexL&hz5QG
zZ)au<t-4%xcQP7!BX#CnR?|CL3D25t>u7CRYP&Ux9f=>+Jt=Pu$^1_I$=oVbSNoko
zp%UiXJLxaXePXygZi<{#8~(Hw9?Ic^ESs8I5b;X6mrPH>xk;1OiFxq^TVZ^nlPStA
z1#f(<&W#r^-)x*1$)<HBEIeYQG&#r{L}0DT<v@~4^-w+F%-w4^VOBg9VPc4GU3fji
z$T%?4EL`4{JFJg7yDG6gmVG&Ms08(-sIls6xaTsX0H%=hu@U!rOq%ly4<#^fX>oH6
zANeS1iZiz7W{l5eH#S$qu@x4HZdXnXlUJt3s#?}N8E|%2^pe6WplVa}iz)DN+VzMY
z*!Jz^-C9g(9V8>vS4Ml(`J0sFsLx-D=o~oC*6M4e%ntV$wW(i_xANqMmvRrN^GjC{
z*zu-tREG;2)RilXv9NC_D}MqiM=Oa}3`|)@3()c*xc4!^{zeF4H!7}6wLY%h#$#9h
zC5UGKp3m>hGGI*AZ%mBF%3Z^~mnK_NqxNlHJ$(;p?1c$$@&S<{!f+A*U+Mkkw$Jy4
zTLIW`=onX6<#4Tumib`9PX!%W!|Q~j!25W)d0|aiQ>Zrjh-g}P!6mjVNUX%ry})9A
zsRjEj&s&f;1~LO1ex}9XyLsrZkTAa7ctOuzjZI&d*{kk?ASYvyD|`;V8;pFnkpLbe
zM=_(Ur1xRcILq}*Oi}C&9TN>BuI7}RzDT^v?vPrs(h;(43MY^!({<CxNCT_U0h<{B
z6|tv?1Cg#!cW+tmQBjw9VTs6vZj<!4;G@+-p-`?XLEm4cHW((Sx<{VDXF#FIwler+
z$9P!@a_fCR3z8p*%lJw3(@W{@od*X)E$HVR$k_@1O%*<v(5BoF`2MHI6py%FuW5m~
ziU_pjM|7%lfrvdva;!IsGoeXXMK8WuJKjqvwnH}f^nRaUW>o-mtedyyYYJAa+4^~p
zFNH9&$wY_-nicluJZpYbIHSY1KJ4?LJ>r2Tf&FR^*7hu{JVVMyhZ-?Uz$QP2;%WO+
zE&W~e3!DW_H}&AM-uv96;v>boZD+(X4=WxO;4DduX=2GMDX<ve%%wV%#%=}_gOQdV
zSIjnD^{4l=k2-IfvS*7;#ndA_hR^8x<Cz(5QxUt1#4`fOVPILdaNBN1)7H8O=vc#V
zsXqG_Gq_l*Hn6(Nx7@mDhd|gSH<a{dEvnGv>cJcF7U>d9bEKl^;iBgrZ?e|5)SOP@
zQXdpbLdqoZk?Z#kNM{rCH?JW6eYU(2Mb)^cgI@ZE)^%{--Wfj58(wL7N${qg77U}E
z_Ioh9Xz%Ol19h6Y$ob0rUFPnh7jpR!I~CvIc{!t7o<0f?j;#une>~OA*op_o%~SI!
zSne5OtZ9w!hH`XEEUjmPvQ`thqVrSjPLh1wW)zbG<RX!-v7Y)1tKyq;Z*ZNtjQMdR
z_YE3zpHjEqOp#yEwT$l=EPoZ~CY~N{%;8q`8S?on62Qclf_Vm9G`HR7=opJFKe=dc
zaoYwy14;s&QS)GH;Qb!HpQral&ozLnnghF<iZbTFaXfh{i^o8xr;ftD#->PfZ3_i0
zneE&qn;h=iS0AytX$B=dQajTot&1C{oZo-WjDzTPgfx=qM=p_?P{LGr^6maAihoC*
zS1heg6e?|zpB61ERs@~THZh*0)Y3TM?SiFR@vE=*47%Ye-JU>NlfPpiFR-pC3ES?S
zP&?HM%>qZ&&g^LEUOzbFcsC?%RJ6UD{*CGOG!W}l3lP6lE&9lmQ^b1gmWjo;WtO6+
z5ABb*D1^Na=S!MxFjhL2a5*jv*V*I3{-)#u^V-6!$JTr9{v8u+uk^`WzCf7jl=OUa
zTFT1$#9BtY!A6kV%&!e=pc;U3T&&%*S49DzM#H6UT}J?Z;2B`lB3O_uV=dQUBK7@K
z<zy%k_2x$owBa8|AcfU2bl$j5nhxl2&B5HxK5Gq!5RI;pb;Uufz#yeWE%VaTcebqs
z6VsZjhuGyLym>i|<@o!;fY%?OOZ5ckW4Wi2QNQN!gZhF~$w5H4jKnh@d~A(I1uJ?r
z|6_xBhl@5OXA4cjk%qLse3`vq>(@}(ThOpzt}_*6^X{#;zT9C_k%Y&9t^!CYPHDs9
zoG(;U+2;?=9rrn{#&wOnhnjt2^*;Q`?E62rbdZ?;F2+kQQJZkv$4BhiyT#&qGF-f9
zME<RFoAh^k)SO>6I8kWd_bK$&y>eQI2PfE$NVc!9Rlg=9RdG1&7l&^FWxZF~)KzsI
z=xC<po29n5{y0WuKN#^2HZ^w3KaZQ2hkP638PKcxba)j8yniW&h*~3iQ}Asc2*o6U
z6EAS&>IGc}ffMr3YF6Iz^?v-o{>VU_mR5LbbgFcMWNA&T{rx0YNQjAW&Fk1)yanSC
zyB@(rO+v^)Mdt0A6xVhy&nD)jmZ!h$uH_uH+HdqLDpehj<|@z$47s12x5Wn+OZar@
zFKsHiu9L9H=@UE0B`DsMWQM|_>ZLpb_%roKmXdZ~T*Y4Wu_&uZPD$b?gxuim+Z%><
zYJo)oc~Vm1r#6_!wB<|>9@XOH78%;3mSkV--`<-S-ivgC^{Z3mQKV96B9Qng8KZI|
zHL~ognt03BF%rYJrcSqk>k|QBliO-Gux7}g)&|!-D0RU_LcX@Gaz$hJ`sbtqOMuD9
z4TshRo8$hbt$YAOG%B3{`{&-Q@v}5)1O)}MvYG>P3q{2qcn_xBq<h(9v#Ks9M^bbZ
zDbwR()!4bs^+lWP_>=1U&}TsX$-Bg$c!N1SOLSe(*l~$S(BeVzVM?YdgsPfSfqQL*
z4KWr9$)Vj3PI)p&pJP*DVX&5Z!nwwbQ#uuGwq|BW)dZ>sq-<l}6TCQ0!^~ZUEeY{D
z#UnGgha{>6UF+{ISJJ%<4!33e8*a|#fq+)kyNMI3sb=<-+>ZJH6$1@RiFf697<xo@
zO9U@&GtYKJ5j`O4LYOJNP!&B{aD@~rOw>_0+W7ECPaJ37-8FzT^Y{G})0Ia*UQi9^
zx@xlc)o&iQzj<2HeW(s0%DdEUYz&=eU?RHk6nAjw2^3a4w@q|B2gMwy-pG_hyEEzW
zzc1Nh`cO>HAH9<to0WzSmI~8;J3E^O=Ippf-&%EX!&>*v9G1dG#`ZrDpDAo$qzXKc
zai;5S(SBW!15!fceR$RWeMX_&KURLz*@-%_5qh3snCv+9X=mS@Nx8Y?E9^((#zqU`
zo3~Kvq&g?j#U}Kn&2&)RAzV@*Q(t^>aO;T@3biOry%(08_wO{5`I2;c4rKGA>2tH)
z5jt<dPIV+oP8+13y4Y9r4r&Q+wVkXDvg45>mAEdEc+m!4D{5eg<~9G$)2n9qC7FGk
zDl}eTH~u>u35Ble#)M}5P<O4Teo2gG#d}unA^eOgK2a9I%9=Hg$zuhi;*|&Do_^r?
zto<)-sQ;HX6o0@4vQc$Cogh_O6q)MGr(()37|N>4WxhL|XO0Y@DPq!xG=x3{G9=p2
z8$UU{NQ7%-x>UNoxO`c3%-^jhN}QiPQxGn*i>#2LhNpYb#n!S?sjG3DWZt3>9b*am
zIGGCZJ0U;CG=;&##g;6PHEZSu3%_PHeKzIc@5_Cl!j=M3xkXKB>!yaPM#@5WtBv;b
zzEHkA^fA=K%j|-xCttOvi4D3D!>~bky??Tlqk}Dw^*9BEXcH$yxV4gTNf}4yMs8ec
zZfiZ<gx4w8K!yjLGb2ov25n{6`BVmF&Z6jtEDZ@IfifiUB|oaS?>jV?(F2<a+zk_m
z@260V!skmu;(&}8$s9mA+U`B2%A8lPgzSp2zz_SVIcs6TiEU_Xu)fj<3{<SnthH2g
zHTIq9L7sB->dS|{?1ED*ddhvKW8G#}OByo`53c#SLP1NUXvU!Uf)Rco#08ryR0_}O
zL~NgNf;MVd#nSeLn?3*HW~^<_|K_G)AsDmHM-yLul3?OQ>&nz!%(2+E${|3<6*878
zd=S_S#Zy&Gx=*4nq-MG2B^oZw%hyfFlT>7$)f}WcBvD%sHR%YlIMn7gOq8Rh@*H$@
zXu3PICBa`ed87vSeB9rKi~KLBd4&s3<tFO=gWCFU5R`mbsk~mBKU%InK*xMK+AR7o
z4fytQQdaI&?zN51Y11}`q$Sfn{&{+1V@R7>Aev|~Z(iUmn`WKqi@cY}8m{Yd&>uLH
zOw3xO@2lTt4?%jo<W1c3y!~y%c&)FpuR+D`5#wI_`%26qL%ppl?+7aDcq<~)3wZ~=
zd9jqt#^nZ>>enZvEEQ3FK*jrE&HhViNT(>n+0#Nuj_X|!1W0Xu?Co}AovjELQRGx|
z3wF~SkYF#|AiavT(vE#SNHVr4$7Y0;(0%dc3!g|mimfNtbw&zPwIa(}PL@D>QF6xv
zhhNB0Qj*q;vImG)Vx*NaZfGXq(aZ~7gFC37ken_cr)?2znOY+TiHTS0(hpD-*M)i(
zp%qyq<E~1fC+x9F1;lLrdXsEY8!?wqW?9$&hjY{`t_&ZZ0rpSi%y~e8oIB(>Jf)PS
zU7jK?1)fwxaB>XyMGw2*J|zL)cE&_wivIMiR7o}cjvP`Ia#kT|7(^Q=J65;J5eK9e
z;ZpMu_3Hm&NkGW0nYc6%V9UI;X!GQ$*(b`G$sesypYv85-G2A4ZaMrjU|fy6I-Q0m
z6_ww3_~xQDoBcdBth4_z7~IW5spLvWH21k2Qt}-VoPQo*YqAvy*J|hlsp`z+m%33^
zIGUH}k}rzb$-G8WWt&?Jk-SQ2=}!BHe%bO+aEJhwTg=1+th|Xus6)>#IxE}vYfu?-
z?A^;gEon0DmT<GME|j1g2%&iMf}+n0isQ9Zu9-znM%Fs)hXscf**^ylXR2_B;@;El
z=9N#$Ntj2Q*9@Kz?OkSlwZC$HW82vu9J@GSJ;fsNdi_-^;Szg<{>1iF`5E#;^-d8?
zk={D)YWWU*tJdRSGw+e|n}Li>(?h4W?!uz82et@pA}og1_aW=rkre5ep`R|mPD`4g
ze7~xBxs06hu7mw05*iroYRFPWwtTg$J%W4nnS+K?gyhcNBx=U8N{HQKV(?`X?ybEI
zhay3lS01!b;17y{%I=hXDkGDkjvLK+&L;D4_nLgkG5r6aNYVTciZQ*1r_Sm}$2&4e
zm#`2^!i&^F5~woOZ<pNz#fIu-6cnq`@-AlxdMf2G{gx0%qAfixSd?({({IIe@%%+a
z0R2zS&A%GOa9Mc7GE+HRGPAZBfkO?+KM1d=>S7Mlu%EKh?NO(o^>pZS)Sr*-a29B^
z2hzNawqm6ZjBR434W{(`ABys`j#$^y4@Rl}!012o4~z}}hB1>7N~}2C#_AR`FafV%
z!qq<rvJbzfZqvI8Q*0xDC@PSiCuYNW@Dz8{!2w6};_(jnI;?R&0}Nj)d}zNdW8=e%
z!@j%{Nl}f%>h@3z2_3k!Xy|Pq<V;De0H(HGI~0J>xm0o+B_Bc)!ZM;pot;z3jL&YZ
zskf@PE#Xcgy7Z+)0(l-HO6`xovqLv(piolcJ8tiF;2TUX#oT$N&Op&qAq*%}9!s-@
z9vhpD7mqRx@&@fujI;=Rc#k{D>Spz+qszM%3^_LaaWuw+I+@CB`z4FIh`HG^sj@!=
z6nF4{N>|E%?7fZY*OqseT}ClB&-^Innny5cE09|?*C1TRe>jhspLBYY{9STiF}MQ>
z7%+S6H0J$<TC`z>Z?Te43Uv1exy^YkS30k`3dRyU$le6O>~9WvnQhHuVh}WQ`72G2
zE>ziuYUFB?694Z2*(qAEK<Lu)GY{JJE$?7Lec;0%=+JS&@iP4DA0`X1oMLnQ8*gML
zP3oJ_pi@NsgY?l?bGfMQc?&3)RN>d@Po6JDMawtk7A>VCFFRoIHWAr{!*QL00BQ2!
zT-~>gRR}y**3i3+_~YzULRI3S7DMP$$c?IEBJ}m|{JIwgrjCpFS#|xqp!WuLx_qsA
zeNCzxm!3>H25+THxla;lc5%=1txtAK1)LJB-qot#D30{u?gIz+$amy*emhmPJeFBB
zdjT#woVN`1ZhWTp4XL*$z9F{1bC7e^bs3MS)`YS-@%zOxm)RIAU(aUdCggi2%uMBt
z^q=nrA$Vo^=?gfm%ck-U9Bo-<;6DSz3|eg3C~-ENN&0!eq$?LZ&fLcIX>(+`oNm_!
zeDhseJsP?`nO-<t%mT%`yd*2qx8B1YFUiVDY(eAsaPF*a?)Q5b1yDqS{p5|uVV*h2
zn$mYVaggM@<uHS2m?@He;}X|xOh{l`WuV)uGA#ZDNVdJKb!jT+?cbEj1Gi2(@5V`k
zc&w1twR*Y*=46`l)a)h?8&?;F_??M;Bcl9%)|hZBF19(l;1^KK{l=wjl$$Y$;oSjO
z@O(|+Fn<Q@3`Sg+KOh!O93G40Iv0%aN^EZ~*<`tk-Lc;xB}$7^Nr0aLV>ao9Ko4A6
zjj}PXgBq%I&|Q7q-1=8QFYz2pAG;UAD0RK;u$Er0uZAZ*+y*K;!{n%0t2z6yXJ1<h
zvFWFHyPX7^sJ0EZC)DIrqpJf|jA5Y?7{sfaiDOdcXMn!gLk@EGzk$fAC;kTrCSK0Z
z>c&UJS-haKK{;XmP|Y`yUK(J`GC3C`f@&^JvzG7Jcg}8~LHGse!t2CG)ZWfd<P1CZ
znu`2_Bc}pG(<{@-{|SKWzW@ODD=)cy9XRBe5PHNG)#2Cgl~CmGlW(n!=&%fp{n_5+
z3)vl~-9A>C`#m7owK6Bv>}MFHYdyoip3<$&?O^x!#x!+Q)ua_@Gl&D`7>!KEJhEes
z3O|*IiZUs;y2?S%Pu=jbWya>{)}m6pOIJX$HMdrI+#FBWWpcJ#!BrB2qslw9%KdTD
z*yw1nL*7D~U#k3bW&vUhq!Yp?=M!eIitOt}6-wHUdPQoFT;M^_OQRJ>ZKOCC?$VOK
zU6S7*Wn%%M8^jHC$SM+mr&q^HFCG5uHSpBA%NF?GVf}JOnH=xj7_>*cxv)uO%=9sr
z?!-ELTR^zrnqIBOJCouC)l|XltQFg!SMR9?4fWx^UC=wsce;=+8KeiKq!Igj9T6u>
zdUC%(xDOY=?irPw8!u~k>lO1tIO8r$YayrDX8>+VHM7lCbcdEHhaWugOxzU+GZAU~
z^E1HNN7+-ljqi^Z3fXDQ2@wUlLVNL%n1a94gpd<}gB=I4@2bHXU}T3?dz1?^w(01=
zkKrjn@%RHv!UC&oPH5ws`inL>TpT|Hs{bKdYif^m7z`3m`!j$=(_7;uhzwfRo_k61
znAV;F9DlMr!=F?k=OX^Iw%L9!C!Q4IU${2rbW+L6aKVhMMpjtpp&x+LC}AAgQEZ6E
zMDG|50B}T=<4pl87z@)%V=-QOly8Ii20`k1Y+Rxjc^{~H8K;&qz~nL`NKWHQY!8D*
zAMa<p7_zVGX`})<H43UKvurxqtVPS*+?k!$wyD#|*9TOUT9b(88)#K2ow*$@^>#kq
zH%fl_i@<<ybB#ia{5fV*O6u{J;Qg^3XXRN-l$;zd$_d){8~70u`b3<^3WM{jIc2s(
zQpAya5l4GuAmB4V^-CMwbQO&%?%bG6O6HPEti>;drm<c%T8=K8Vuq?kt=Ykpk6xcX
zgri3HSIl}&$pP(#t+9?AAPel#)yvhtk{SlHPS)MIr`2t`4v(7(?CDz#0;_ok_3KR^
zVw|u~p>cz_s)dndU56UWNG_xZCSyWmRzU>8397^r>bh4LQb3w)2QFG^sz8!4Got=a
zmI?On5vWP>{1*+)>N!%`zT6r(^b|e5=Fl#X9w4uT@Lk{j^nV68rUm#e!#F$xdXy?o
zsWu;kZk)<;64YEJEbV4tA0#4hDGU$f3c@(-)DiMbfr0`<k+Qey3rdhP*!9cG0GZ`V
z2b%ot({KLVqc8=>Vl1MFoJxwsb9%}r*i9y;4BW1={;Sos0et9Z1-9YSzwJm=Lzs-%
zSkexw1wfLw>Ji$}bG6?CejVx3P^Z_wP2gfmE2C7)zGubyzUp6;bL>rmzTx@VLFB%6
z8Hn->sCXi$pWrLje{l|kI#wgJ6Aq@?qlTop8-FL|jwzY;f$MTBL!V_3s<J^NZeKL1
z0?<k}3+Nz60Ay@97R{nw(?)jHdPtfGBcE)7=b-t~xU^*Je>zuEYIQ-NxqCnJef+(E
z+D5<_a`A_**qR!8l{(|vHOF#FQQ$6}#RyqeRz&qto{^>9;}0P^qhnn~=P2{f$IiOW
zvyBzOUBWx;urRYGF1?Z`zb@?;$4ZK_1F1g0n}$q2-Wcu<m!;$}c##&BX%aNi-eBU&
zg0+4@6r*VWDvkeB7=>{f{E)}EIk?1bI52g-V54kvkXPulL~p(+IwrXO>f@Pi@Q?aY
zC{QSau<7OC06Ch{?)k6kFAL6so$TAxcHr?HJw&tX#!s&G1}i-%J>%Pa3Y&+u+f5W^
z<x;xlh2e#1kR-7#-hb5WmmQs>1Uv!A-$yz9pSWyaRh<X-j6(wt^O!-{omareky+&1
zKJg8YWS(CfFNfbPW!}TIG1X>;D?_>;*t@p&{Z;sn@f+Mc>8>rwb^h01Q%;LqOl8(-
zy3dQ_J&SI^exq*(K#ac;v`zUmP?;kVykLDN2>0noijKdt3I*&_t9oKV_fyt?EwEJQ
z`L%J%YKOG4EuT?28;5kLuAnA918QrrnPzVh%T|0nbDbOHT-53>?^I_xdE=_kAUC;f
z^XJEkb)$BvKYQfFUPqqTS!zrRGm)i!L&X(ck}$@ix_#M<EL+lRG?{linkB!ct$Qy9
z^ccof(d?npwyKuDA3hd(2E=zi_GYjKN{jiR-S^mN5;;niaUG;w)LKCCA7xEPI6l~b
z@ba7`c=PlK@P38jO^2hI!FXhSGT@B`O5r^Uyc{X`FCy}<pB!@5OI;Q!Q6_`NQ60J`
zI8wRFtTiqgb@56t3Z#aP8{H8qCsG80?Y~!5_=MQA9ZW@_9_^2bcnz^^sn&s{DzamD
zaC)IfmMQjRjsS1uo!#R*Y{pgkIa^T(e5Y4`wDp)5@FeDZcKpoxcBXQiseXF0gW&^#
z!l-%HH*hE#5t)G(W^k{Qv`Bk^CiRL5Hrp?^ez!vDaU-Zzh!P4zq9v}v*i`v@%6Tn8
z7gAWE9}898v;)*NkVt|=oXMH>9$wi1O&4w7NfYxx`)Ps;?I+A()*Tv?-H4*3VHGLE
z)Qx8V8tG{-5h{N?eaZe*M$3A>ut@uf;_kI2jmKmlBv<ao$fDP|q0Hui^oK)gXSsT-
zD#(R*;L(ta{;=cffOMO%drCjyIe6`;Hv_C)#`|r)g{DlrAW{jx%4Bk2a^RqzviPXX
zK)}u6cxUp*)CVPMQ>jw728-5AiI=0R-CvP3%;v7;h2ijiHFT$zSm{|&cCQx}-cJ=q
zYBfkPg$KtWc3?Nr4Kz0lM<Nu4+xv~Nt$c?&4aI4ws8K~GMlW(sz$%HLT(Cf7Y8b~H
zM0yip`>Rqhn8To5zhr|{ZjPDUNvIG#Dc}rk_K-L57B?o|V-m$KZG1T>$c~`+c(Ptb
ze6-;iz-WX#7}03lgy0OUjhhiZpUpES8wskE-HtwZz1Iu38xBWUc->?;mm7pj)a6Vf
ztJ+_u^a(=la6mdShj;pacAb9;n`EZ8OXvI7fnsucMajQ`u?BQzm5WAhacLywwNcp<
zPKMFqj$Ev^8X60Qc%ghVrn1f314*so<%ZI|qO70>ZTgu7xz^3Wz{2{@vHb@GQ)%Gk
z1A8<P8DFxOCQ9-~c#nn-E9OL*H6z2awD3SwC0FsB7F9G=>hRdP%jffCa80*slIv2l
ztuAulqz;57wR)0jr;)wGJ9j^cu)5|nT~!GNUrhMB&HGbM|2@p9qHlx7R^@9pw7ubC
z6GM5CpF9pvxh?k2*g%qb?(+p9wN1E@<(TdI8^QNUVtjUis#4tg?TOJCu@BAGtIZ0i
zV&Zw#f}uR|^Te;N1`O~j7I6~IWa3Xk?;PI_(@11gEBVkls}p<*!KflUvA0CRrQ2E&
zVUsEjW29i~3#cqD<g{zC-yCpkswaAKHdWgw985Idp)@wPH_SIP2DMHJyXY$^a%=nG
z&$dsWf>f?d<d-l64a&^tW^lj0olowRu%mz{MP9+n_#Y5?1q)j_?%HJhkjl5+qsi9V
z8o4g1F|SUDI?ik3NYwd?U*z<f?MS78PJ};c%VIvnAe!aNGhmVb$n5Hat=2ktBKmD#
zl?bi%O}@|4$=h<6kHiuQ0&kMeGwR||YnBQ<QI1RL{rigqDRNqYj8cJT#*_id!-=-M
z>Qq(qiVcF!HB)tkX|;)Khag7RK(wAbSoG7P#5GTP6+Uc&#Mgez?7}rg7N+!aWUy^`
zhLVLpGX3KG9Q671fhxJVM0!IwyhUaOZQGK!)^YRf?gNec1^8GB)sBwk__FPsF~J;t
zo0b?r)psO`G?76TB|)*`Y-KCQBxIf0v}U}rKMd6At&4s*W8Bm6ep|aeNA=2BE~cpq
zu&THsK9jAvb{7vbzK9~e^LQnf;7IrkVAtMfxJz5(o7hr0ZcLhQ&}J>;r+1@-X}3Z4
zAdn8rpZ@fTLip2(M_QAtCQ!=ZivRy6qyJgtXF+3|8Wx`58MJjnyyC0W$uNajHtrPy
z?y9~Xs+|Qg{a6)LV{?*+)|rTA9mpUXbtsY1SmJbL+@q}or0;7|RUT)$H|Hg+Oumm&
zU9&3sdHBGUe)mak=;tj`ec|%y+Fr&>i>CI;Xm@aYmz;4qG?87~2Q1R1{jfG^O%V?1
zi6t7EOH4pPV59vuHqnL_kma)e8p(sSXYsYk)s~>0oMQgY^@q_gCrXX`(K%a+uW=kG
z;cga?It!gD?NsN{_(aS1gi|usp$Yi~fiPHTe55c{`Rg0qvBnBZr;4?G3#R<Qy-!!z
i4nwsq?#%D0XA}NzmHu4<`0tU!Uwzg8=MQ^7&;2iLL-(=(

literal 0
HcmV?d00001

diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolverTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolverTest.php
new file mode 100644
index 00000000000..99e49e46caf
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/GalleryEntryResolverTest.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use \Magento\Catalog\Model\Product;
+
+class GalleryEntryResolverTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var GalleryEntryResolver
+     */
+    private $entryResolver;
+
+    protected function setUp()
+    {
+        $this->entryResolver = new GalleryEntryResolver();
+    }
+
+    public function testGetEntryFilePathById()
+    {
+        $productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $productMock->expects($this->any())->method('getData')->with('media_gallery')->will($this->returnValue(array(
+            'images' => array(
+                array(
+                    'file' => '/i/m/image.jpg',
+                    'value_id' => 1,
+                ),
+                array(
+                    'file' => '/i/m/image2.jpg',
+                    'value_id' => 2,
+                ),
+            ),
+        )));
+        $this->assertEquals('/i/m/image2.jpg', $this->entryResolver->getEntryFilePathById($productMock, 2));
+        $this->assertNull($this->entryResolver->getEntryFilePathById($productMock, 9999));
+    }
+
+    public function testGetEntryIdByFilePath()
+    {
+        $productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $productMock->expects($this->any())->method('getData')->with('media_gallery')->will($this->returnValue(array(
+            'images' => array(
+                array(
+                    'file' => '/i/m/image2.jpg',
+                    'value_id' => 2,
+                ),
+                array(
+                    'file' => '/i/m/image.jpg',
+                    'value_id' => 1,
+                ),
+            ),
+        )));
+        $this->assertEquals(1, $this->entryResolver->getEntryIdByFilePath($productMock, '/i/m/image.jpg'));
+        $this->assertNull($this->entryResolver->getEntryIdByFilePath($productMock, '/i/m/non_existent_image.jpg'));
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceTest.php
new file mode 100644
index 00000000000..48506b08ed7
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/ReadServiceTest.php
@@ -0,0 +1,473 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+class ReadServiceTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Service\V1\Product\Attribute\Media\ReadService
+     */
+    protected $service;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $collectionFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $setFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $eavConfigMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $setMock;
+
+    /**
+     * @var int attribute set id to use in tests
+     */
+    protected $attributeSetId = 100123;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeCollectionMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productRepoMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mediaGalleryMock;
+
+    /**
+     * @var \Magento\TestFramework\Helper\ObjectManager
+     */
+    protected $objectHelper;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    protected function setUp()
+    {
+        $this->objectHelper = new \Magento\TestFramework\Helper\ObjectManager($this);
+
+        $this->collectionFactoryMock = $this->getMock(
+            'Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory',
+            array('create', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+
+        $this->attributeCollectionMock = $this->getMock(
+            'Magento\Catalog\Model\Resource\Product\Attribute\Collection',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $mediaImageBuilder = $this->objectHelper->getObject(
+            '\Magento\Catalog\Service\V1\Product\Attribute\Media\Data\MediaImageBuilder'
+        );
+
+        $this->storeMock = $this->getMock('\Magento\Store\Model\Store', array(), array(), '', false);
+
+        $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+        $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($this->storeMock));
+
+        $this->setFactoryMock = $this->getMock(
+            'Magento\Eav\Model\Entity\Attribute\SetFactory',
+            array('create', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+
+        $this->eavConfigMock = $this->getMock(
+            '\Magento\Eav\Model\Config', array('getEntityType', 'getId'), array(), '', false
+        );
+
+        $this->productRepoMock = $this->getMock(
+            'Magento\Catalog\Model\ProductRepository',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $this->attributeFactoryMock = $this->getMock(
+            '\Magento\Catalog\Model\Resource\Eav\AttributeFactory',
+            array('create', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+
+        $this->mediaGalleryMock = $this->getMock(
+            '\Magento\Catalog\Model\Resource\Product\Attribute\Backend\Media',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $builder = $this->objectHelper->getObject(
+            '\Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryBuilder'
+        );
+
+        $this->service = $this->objectHelper->getObject(
+            '\Magento\Catalog\Service\V1\Product\Attribute\Media\ReadService',
+            array(
+                'collectionFactory' => $this->collectionFactoryMock,
+                'setFactory' => $this->setFactoryMock,
+                'eavConfig' => $this->eavConfigMock,
+                'mediaImageBuilder' => $mediaImageBuilder,
+                'storeManager' => $this->storeManagerMock,
+                'productRepository' => $this->productRepoMock,
+                'attributeFactory' => $this->attributeFactoryMock,
+                'mediaGallery' => $this->mediaGalleryMock,
+                'galleryEntryBuilder' => $builder,
+            )
+        );
+
+        $this->setMock = $this->getMock(
+            '\Magento\Eav\Model\Entity\Attribute\Set',
+            array('getEntityTypeId', 'load', 'getId', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+
+        $this->productMock = $this->getMock(
+            'Magento\Catalog\Model\Product',
+            array('getMediaGallery', 'getData', 'getMediaAttributes', 'getStoreId', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testTypesForAbsentId()
+    {
+        $this->setFactoryMock->expects($this->once())->method('create')->will($this->returnValue($this->setMock));
+
+        $this->setMock->expects($this->once())
+            ->method('load')
+            ->with($this->attributeSetId)
+            ->will($this->returnSelf());
+
+        $this->setMock->expects($this->once())->method('getId')->will($this->returnValue(null));
+        $this->service->types($this->attributeSetId);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     */
+    public function testTypesForWrongEntityType()
+    {
+        $this->setFactoryMock->expects($this->once())->method('create')->will($this->returnValue($this->setMock));
+
+        $this->setMock->expects($this->once())
+            ->method('load')
+            ->with($this->attributeSetId)
+            ->will($this->returnSelf());
+
+        $this->setMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+
+        $this->eavConfigMock->expects($this->once())
+            ->method('getEntityType')
+            ->with(\Magento\Catalog\Model\Product::ENTITY)
+            ->will($this->returnSelf());
+        $this->eavConfigMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->setMock->expects($this->once())->method('getEntityTypeId')->will($this->returnValue(4));
+
+        $this->service->types($this->attributeSetId);
+    }
+
+    public function testTypesPositive()
+    {
+        $this->setFactoryMock->expects($this->once())->method('create')->will($this->returnValue($this->setMock));
+
+        $this->setMock->expects($this->once())
+            ->method('load')
+            ->with($this->attributeSetId)
+            ->will($this->returnSelf());
+
+        $this->setMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+
+        $this->eavConfigMock->expects($this->once())
+            ->method('getEntityType')
+            ->with(\Magento\Catalog\Model\Product::ENTITY)
+            ->will($this->returnSelf());
+        $this->eavConfigMock->expects($this->once())->method('getId')->will($this->returnValue(4));
+        $this->setMock->expects($this->once())->method('getEntityTypeId')->will($this->returnValue(4));
+
+        $this->collectionFactoryMock->expects($this->once())
+            ->method('create')
+            ->will($this->returnValue($this->attributeCollectionMock));
+        $this->attributeCollectionMock->expects($this->once())->method('setAttributeSetFilter')
+            ->with($this->attributeSetId);
+        $this->attributeCollectionMock->expects($this->once())
+            ->method('setFrontendInputTypeFilter')
+            ->with('media_image');
+        $attributeMock = $this->getMock(
+            '\Magento\Catalog\Model\Resource\Eav\Attribute',
+            array('getStoreLabel', 'getData', 'getIsGlobal', 'isScopeWebsite', 'isScopeStore', '__wakeup'),
+            array(),
+            '',
+            false
+        );
+        $attributeMock->expects($this->once())->method('getStoreLabel')->will($this->returnValue('coolLabel'));
+        $attributeMock->expects($this->any())->method('getData')->will($this->returnArgument(0));
+        $attributeMock->expects($this->once())->method('getIsGlobal')->will($this->returnValue(false));
+        $attributeMock->expects($this->once())->method('isScopeWebsite')->will($this->returnValue(false));
+        $attributeMock->expects($this->once())->method('isScopeStore')->will($this->returnValue(true));
+
+        $items = array($attributeMock);
+        $this->attributeCollectionMock->expects($this->once())
+            ->method('getItems')
+            ->will($this->returnValue($items));
+
+        $attributes = $this->service->types($this->attributeSetId);
+        $this->assertEquals(1, count($attributes));
+        /** @var \Magento\Catalog\Service\V1\Product\Attribute\Media\Data\MediaImage $resultAttribute */
+        $resultAttribute = reset($attributes);
+        $this->assertEquals('coolLabel', $resultAttribute->getFrontendLabel());
+        $this->assertEquals('attribute_code', $resultAttribute->getCode());
+        $this->assertEquals(true, $resultAttribute->getIsUserDefined());
+        $this->assertEquals('Store View', $resultAttribute->getScope());
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testGetListForAbsentSku()
+    {
+        $sku = 'absentSku';
+
+        $this->productRepoMock->expects($this->once())
+            ->method('get')
+            ->with($sku)
+            ->will($this->throwException(new \Magento\Framework\Exception\NoSuchEntityException()));
+
+        $this->service->getList($sku);
+    }
+
+    /**
+     * @dataProvider getListProvider
+     */
+    public function testGetList($gallery, $result, $productDataMap)
+    {
+        $sku = 'anyValidSku';
+        $productEntityCode = 4;
+        $attributes = [
+            'image' => 1,
+            'small_image'=> 2,
+            'thumbnail' => 3
+        ];
+
+        $this->productRepoMock->expects($this->once())
+            ->method('get')
+            ->with($sku)
+            ->will($this->returnValue($this->productMock));
+
+        $this->productMock->expects($this->any())
+            ->method('getData')->will($this->returnValueMap($productDataMap));
+
+        $attributeMock = $this->getMock(
+            '\Magento\Catalog\Model\Resource\Eav\Attribute',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $this->attributeFactoryMock->expects($this->once())->method('create')->will($this->returnValue($attributeMock));
+        $this->eavConfigMock->expects($this->once())
+            ->method('getEntityType')
+            ->with(\Magento\Catalog\Model\Product::ENTITY)
+            ->will($this->returnValue($productEntityCode));
+        $attributeMock->expects($this->once())->method('loadByCode')->with($productEntityCode, 'media_gallery');
+        $this->mediaGalleryMock->expects($this->once())->method('loadGallery')->will($this->returnValue($gallery));
+        $this->productMock->expects($this->once())->method('getMediaAttributes')->will($this->returnValue($attributes));
+
+        $serviceOutput = $this->service->getList($sku);
+        $this->assertEquals($result, $serviceOutput);
+    }
+
+    public function getListProvider()
+    {
+        $objectHelper = new \Magento\TestFramework\Helper\ObjectManager($this);
+
+        $dataObject = $objectHelper->getObject(
+            '\Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryBuilder');
+        $dataObject->populateWithArray(
+            array(
+                'id' => 26,
+                'label' => 'Image Alt Text',
+                'types' => array('image', 'small_image'),
+                'disabled' => 0,
+                'position' => 1,
+                'file' => '/m/a/magento_image.jpg',
+                'store_id' => null,
+            )
+        );
+
+        $productDataMap = [
+            ['image', null, '/m/a/magento_image.jpg'],
+            ['small_image', null, '/m/a/magento_image.jpg'],
+            ['thumbnail', null, null],
+        ];
+
+        return array(
+            'empty gallery' => [array(), array(), array()],
+            'one image' => [
+                array(
+                    0 =>
+                        array (
+                            'value_id' => '26',
+                            'file' => '/m/a/magento_image.jpg',
+                            'label_default' => 'Image Alt Text',
+                            'position_default' => '1',
+                            'disabled_default' => '0',
+                        ),
+                ),
+                array($dataObject->create()),
+                $productDataMap,
+            ],
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testInfoAbsentSku()
+    {
+        $productSku = 'Sku absent';
+        $imageId = 123321;
+
+        $this->productRepoMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->will($this->throwException(new \Magento\Framework\Exception\NoSuchEntityException()));
+
+        $this->service->info($productSku, $imageId);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testInfoEmptyGallery()
+    {
+        $productSku = 'Sku absent';
+        $imageId = 123321;
+
+        $this->productRepoMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->will($this->returnValue($this->productMock));
+
+
+        $this->productMock->expects($this->once())
+            ->method('getMediaAttributes')
+            ->will($this->returnValue(array()));
+        
+        $this->productMock->expects($this->once())
+            ->method('getMediaGallery')
+            ->with('images')
+            ->will($this->returnValue([]));
+        $this->service->info($productSku, $imageId);
+    }
+
+    public function testInfo()
+    {
+        $productSku = 'Sku absent';
+        $imageId = 123321;
+        $images = [
+            [
+                'value_id' => $imageId,
+                'file' => '/m/a/magento_image.jpg',
+                'label' => 'Image Alt Text',
+                'position' => '1',
+                'disabled' => '0',
+            ]
+        ];
+
+        $this->productRepoMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->will($this->returnValue($this->productMock));
+
+        $this->productMock->expects($this->once())
+            ->method('getMediaGallery')
+            ->with('images')
+            ->will($this->returnValue($images));
+
+        $this->productMock->expects($this->once())
+            ->method('getMediaAttributes')
+            ->will($this->returnValue(array()));
+
+        $result = $this->service->info($productSku, $imageId);
+
+        $resultImage = reset($images);
+        $this->assertEquals($resultImage['file'], $result->getFile());
+        $this->assertEquals($resultImage['label'], $result->getLabel());
+        $this->assertEquals($resultImage['position'], $result->getPosition());
+        $this->assertEquals($resultImage['disabled'], $result->isDisabled());
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceTest.php
new file mode 100644
index 00000000000..9db763c4780
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/Attribute/Media/WriteServiceTest.php
@@ -0,0 +1,324 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product\Attribute\Media;
+
+use \Magento\Framework\App\Filesystem;
+
+class WriteServiceTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $contentValidatorMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filesystemMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $mediaConfigMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productLoaderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $entryResolverMock;
+
+    /**
+     * @var WriteService
+     */
+    private $service;
+
+    protected function setUp()
+    {
+        $this->contentValidatorMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContentValidator',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->filesystemMock = $this->getMock(
+            'Magento\Framework\App\Filesystem',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->mediaConfigMock = $this->getMock(
+            'Magento\Catalog\Model\Product\Media\Config',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->productLoaderMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\ProductLoader',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->storeFactoryMock = $this->getMock(
+            'Magento\Store\Model\StoreFactory',
+            array('create'),
+            array(),
+            '',
+            false
+        );
+        $this->entryResolverMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\Attribute\Media\GalleryEntryResolver',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $this->service = new WriteService(
+            $this->contentValidatorMock,
+            $this->filesystemMock,
+            $this->productLoaderMock,
+            $this->mediaConfigMock,
+            $this->storeFactoryMock,
+            $this->entryResolverMock
+        );
+    }
+
+    public function testCreate()
+    {
+        $productSku = 'simple';
+        $storeId = 1;
+        $mediaTmpPath = 'tmp';
+        $entry = array(
+            'disabled' => true,
+            'types' => array('image'),
+            'label' => 'Image',
+            'position' => 100,
+        );
+        $entryContent = array(
+            'name' => 'image',
+            'mime_type' => 'image/jpg',
+            'data' => base64_encode('image_content'),
+        );
+
+        $storeMock = $this->getMock('Magento\Store\Model\Store', array(), array(), '', false);
+        $storeMock->expects($this->any())->method('getId')->will($this->returnValue($storeId));
+        $storeMock->expects($this->any())->method('load')->will($this->returnSelf());
+        $this->storeFactoryMock->expects($this->once())->method('create')->will($this->returnValue($storeMock));
+        $entryMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $entryContentMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntryContent',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->contentValidatorMock->expects($this->once())->method('isValid')->with($entryContentMock)
+            ->will($this->returnValue(true));
+        $productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $this->productLoaderMock->expects($this->once())->method('load')->with($productSku)
+            ->will($this->returnValue($productMock));
+        $this->mediaConfigMock->expects($this->any())->method('getBaseTmpMediaPath')
+            ->will($this->returnValue($mediaTmpPath));
+        $mediaDirectoryMock = $this->getMock('Magento\Framework\Filesystem\Directory\WriteInterface');
+        $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->with(Filesystem::MEDIA_DIR)
+            ->will($this->returnValue($mediaDirectoryMock));
+
+        $mediaDirectoryMock->expects($this->once())->method('create')->with($mediaTmpPath);
+        $mediaDirectoryMock->expects($this->once())->method('delete')->with('tmp' . DIRECTORY_SEPARATOR . 'image.jpg');
+        $mediaDirectoryMock->expects($this->any())->method('getAbsolutePath')
+            ->with('tmp' . DIRECTORY_SEPARATOR . 'image.jpg')
+            ->will($this->returnValue('/i/m/image.jpg'));
+
+        $mediaDirectoryMock->expects($this->once())->method('writeFile')
+            ->with('tmp' . DIRECTORY_SEPARATOR . 'image.jpg', 'image_content');
+
+        $entryContentMock->expects($this->any())->method('getData')->will($this->returnValue($entryContent['data']));
+        $entryContentMock->expects($this->any())->method('getName')->will($this->returnValue($entryContent['name']));
+        $entryContentMock->expects($this->any())->method('getMimeType')->will($this->returnValue(
+            $entryContent['mime_type']
+        ));
+
+        $entryMock->expects($this->any())->method('isDisabled')->will($this->returnValue($entry['disabled']));
+        $entryMock->expects($this->any())->method('getTypes')->will($this->returnValue($entry['types']));
+        $entryMock->expects($this->any())->method('getLabel')->will($this->returnValue($entry['label']));
+        $entryMock->expects($this->any())->method('getPosition')->will($this->returnValue($entry['position']));
+
+        $galleryMock = $this->getGalleryAttributeBackendMock($productMock);
+        $testImageUri = '/i/m/image2.jpg';
+        $galleryMock->expects($this->once())->method('addImage')->with(
+            $productMock,
+            '/i/m/image.jpg',
+            $entry['types'],
+            true,
+            $entry['disabled']
+        )->will($this->returnValue($testImageUri));
+
+        $galleryMock->expects($this->once())->method('updateImage')->with(
+            $productMock,
+            $testImageUri,
+            array(
+                'label' => $entry['label'],
+                'position' => $entry['position'],
+                'disabled' => $entry['disabled'],
+            )
+        );
+        $productMock->expects($this->once())->method('save');
+        $galleryMock->expects($this->once())->method('getRenamedImage')->with($testImageUri)->will(
+            $this->returnValue($testImageUri)
+        );
+        $entryId = 1;
+        $this->entryResolverMock->expects($this->once())->method('getEntryIdByFilePath')
+            ->with($productMock, $testImageUri)
+            ->will($this->returnValue($entryId));
+        $this->assertEquals($entryId, $this->service->create($productSku, $entryMock, $entryContentMock, $storeId));
+    }
+
+    public function testUpdate()
+    {
+        $productSku = 'simple';
+        $storeId = 1;
+        $entry = array(
+            'id' => 1,
+            'disabled' => true,
+            'types' => array('image'),
+            'label' => 'Updated Image',
+            'position' => 100,
+        );
+        $storeMock = $this->getMock('Magento\Store\Model\Store', array(), array(), '', false);
+        $storeMock->expects($this->any())->method('getId')->will($this->returnValue($storeId));
+        $storeMock->expects($this->any())->method('load')->will($this->returnSelf());
+        $this->storeFactoryMock->expects($this->once())->method('create')->will($this->returnValue($storeMock));
+        $entryMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Product\Attribute\Media\Data\GalleryEntry',
+            array(),
+            array(),
+            '',
+            false
+        );
+
+        $productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $this->productLoaderMock->expects($this->once())->method('load')->with($productSku)
+            ->will($this->returnValue($productMock));
+
+        $entryMock->expects($this->any())->method('getId')->will($this->returnValue($entry['id']));
+        $entryMock->expects($this->any())->method('isDisabled')->will($this->returnValue($entry['disabled']));
+        $entryMock->expects($this->any())->method('getTypes')->will($this->returnValue($entry['types']));
+        $entryMock->expects($this->any())->method('getLabel')->will($this->returnValue($entry['label']));
+        $entryMock->expects($this->any())->method('getPosition')->will($this->returnValue($entry['position']));
+        $galleryMock = $this->getGalleryAttributeBackendMock($productMock);
+
+        $testImageUri = '/i/m/image2.jpg';
+        $mediaAttributes = array('image' => 'image', 'small_image' => 'small_image', 'thumbnail' => 'thumbnail');
+        $productMock->expects($this->any())->method('getMediaAttributes')->will($this->returnValue($mediaAttributes));
+        $this->entryResolverMock->expects($this->once())->method('getEntryFilePathById')
+            ->with($productMock, $entry['id'])
+            ->will($this->returnValue($testImageUri));
+        $galleryMock->expects($this->once())->method('updateImage')->with(
+            $productMock,
+            $testImageUri,
+            array(
+                'label' => $entry['label'],
+                'position' => $entry['position'],
+                'disabled' => $entry['disabled'],
+            )
+        );
+        $galleryMock->expects($this->once())->method('clearMediaAttribute')
+            ->with($productMock, array('image', 'small_image', 'thumbnail'));
+        $galleryMock->expects($this->once())->method('setMediaAttribute')
+            ->with($productMock, $entry['types'], $testImageUri);
+        $productMock->expects($this->once())->method('save');
+
+        $this->assertTrue($this->service->update($productSku, $entryMock, $storeId));
+    }
+
+    public function testDelete()
+    {
+        $productSku = 'simple';
+        $storeId = 1;
+        $entryId = 1;
+        $productMock = $this->getMock('Magento\Catalog\Model\Product', array(), array(), '', false);
+        $this->productLoaderMock->expects($this->once())->method('load')->with($productSku)
+            ->will($this->returnValue($productMock));
+
+        $galleryMock = $this->getGalleryAttributeBackendMock($productMock);
+
+        $testImageUri = '/i/m/image2.jpg';
+
+        $this->entryResolverMock->expects($this->once())->method('getEntryFilePathById')
+            ->with($productMock, $entryId)
+            ->will($this->returnValue($testImageUri));
+        $galleryMock->expects($this->once())->method('removeImage')->with($productMock, $testImageUri);
+        $productMock->expects($this->once())->method('save');
+
+        $this->assertTrue($this->service->delete($productSku, $entryId, $storeId));
+    }
+
+    /**
+     * Create mock for media gallery attribute backend model
+     *
+     * @param \PHPUnit_Framework_MockObject_MockObject $productMock
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function getGalleryAttributeBackendMock($productMock)
+    {
+        $typeInstanceMock = $this->getMock(
+            'Magento\Catalog\Model\Product\Type\Simple',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $attributeModelMock = $this->getMockForAbstractClass('Magento\Eav\Model\Entity\Attribute\AbstractAttribute',
+            array(), '', false, false, true, array('getBackend', '__wakeup'));
+        $productMock->expects($this->any())->method('getTypeInstance')->will($this->returnValue($typeInstanceMock));
+        $typeInstanceMock->expects($this->any())->method('getSetAttributes')->with($productMock)->will(
+            $this->returnValue(array(
+                'media_gallery' => $attributeModelMock,
+            ))
+        );
+        $backendModelMock = $this->getMock('Magento\Catalog\Model\Product\Attribute\Backend\Media', array(), array(),
+            '', false);
+        $attributeModelMock->expects($this->any())->method('getBackend')->will($this->returnValue($backendModelMock));
+        return $backendModelMock;
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceTest.php
index d961bb8f274..a4128e4d975 100644
--- a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceTest.php
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/ReadServiceTest.php
@@ -36,6 +36,11 @@ class ReadServiceTest extends \PHPUnit_Framework_TestCase
      */
     protected $groupListFactory;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $setFactoryMock;
+
     protected function setUp()
     {
         $helper = new \Magento\TestFramework\Helper\ObjectManager($this);
@@ -46,8 +51,15 @@ class ReadServiceTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $this->setFactoryMock = $this->getMock(
+            '\Magento\Eav\Model\Entity\Attribute\SetFactory',
+            array('create'),
+            array(),
+            '',
+            false
+        );
         $groupBuilder = $helper->getObject('\Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
-        $this->service = new ReadService($this->groupListFactory, $groupBuilder);
+        $this->service = new ReadService($this->groupListFactory, $this->setFactoryMock, $groupBuilder);
     }
 
     public function testListGroups()
@@ -60,6 +72,17 @@ class ReadServiceTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->groupListFactory->expects($this->once())->method('create')->will($this->returnValue($groupList));
+        $attributeSetMock = $this->getMock(
+            '\Magento\Eav\Model\Entity\Attribute\Set',
+            array(),
+            array(),
+            '',
+            false
+        );
+        $this->setFactoryMock->expects($this->once())->method('create')->will($this->returnValue($attributeSetMock));
+        $attributeSetMock->expects($this->once())->method('load')->with(1)->will($this->returnSelf());
+        $attributeSetMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+
         $item1 = new \Magento\Framework\Object(array('id' => 1, 'attribute_group_name' => 'First'));
         $item2 = new \Magento\Framework\Object(array('id' => 2, 'attribute_group_name' => 'Second'));
         $groupList->expects($this->once())->method('getItems')->will($this->returnValue(array($item1, $item2)));
@@ -70,4 +93,17 @@ class ReadServiceTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('First', $result[0]->getName());
         $this->assertEquals('Second', $result[1]->getName());
     }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testListGroupsWrongAttributeSet()
+    {
+        $attributeSetMock = $this->getMock('\Magento\Eav\Model\Entity\Attribute\Set', [], [], '', false);
+        $this->setFactoryMock->expects($this->once())->method('create')->will($this->returnValue($attributeSetMock));
+        $attributeSetMock->expects($this->once())->method('load')->with(1)->will($this->returnSelf());
+        $attributeSetMock->expects($this->once())->method('getId')->will($this->returnValue(null));
+
+        $this->service->getList(1);
+    }
 }
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceTest.php
index 9d8c7e50747..95e658feb10 100644
--- a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceTest.php
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/AttributeGroup/WriteServiceTest.php
@@ -43,6 +43,11 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
      */
     protected $group;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeSetMock;
+
     /**
      * @var \Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder
      */
@@ -67,7 +72,8 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
         $this->group = $this->getMock(
             '\Magento\Catalog\Model\Product\Attribute\Group',
             array(
-                'getId', 'setId', 'setAttributeGroupName', '__wakeUp', 'save', 'load', 'delete', 'hasSystemAttributes'
+                'getId', 'setId', 'setAttributeGroupName', '__wakeUp', 'save', 'load', 'delete', 'hasSystemAttributes',
+                'getAttributeSetId'
             ),
             array(),
             '',
@@ -77,7 +83,17 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
         $this->groupBuilder = $this->objectHelper->getObject(
             'Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder'
         );
-        $this->service = new WriteService($this->groupFactory, $this->groupBuilder);
+        $setFactoryMock = $this->getMock(
+            '\Magento\Eav\Model\Entity\Attribute\SetFactory',
+            array('create'),
+            array(),
+            '',
+            false
+        );
+        $this->attributeSetMock = $this->getMock('\Magento\Eav\Model\Entity\Attribute\Set', [], [], '', false);
+        $this->attributeSetMock->expects($this->any())->method('load')->will($this->returnSelf());
+        $setFactoryMock->expects($this->any())->method('create')->will($this->returnValue($this->attributeSetMock));
+        $this->service = new WriteService($this->groupFactory, $setFactoryMock, $this->groupBuilder);
     }
 
     /**
@@ -85,14 +101,26 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
      */
     public function testCreateThrowsException()
     {
+        $this->attributeSetMock->expects($this->once())->method('getId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('save')->will($this->throwException(new \Exception()));
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
         $groupDataBuilder->setName('testName');
         $this->service->create(1, $groupDataBuilder->create());
     }
 
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function testCreateThrowsExceptionIfNoSuchAttributeSetExists()
+    {
+        $this->attributeSetMock->expects($this->once())->method('getId')->will($this->returnValue(null));
+        $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
+        $this->service->create(1, $groupDataBuilder->create());
+    }
+
     public function testCreateCreatesNewAttributeGroup()
     {
+        $this->attributeSetMock->expects($this->once())->method('getId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('setAttributeGroupName')->with('testName');
         $this->group->expects($this->once())->method('save');
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
@@ -107,30 +135,46 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
     {
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
         $groupDataBuilder->setName('testName');
-        $this->service->update(1, $groupDataBuilder->create());
+        $this->service->update(1, 1, $groupDataBuilder->create());
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\StateException
+     * @expectedExceptionMessage Attribute group does not belong to provided attribute set
+     */
+    public function testUpdateThrowsExceptionIfTryToUpdateGroupFromWrongAttributeSet()
+    {
+        $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->group->expects($this->once())->method('getAttributeSetId')->will($this->returnValue(2));
+        $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
+        $groupDataBuilder->setName('testName');
+        $this->service->update(1, 1, $groupDataBuilder->create());
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\CouldNotSaveException
+     * @expectedExceptionMessage Could not update attribute group
      */
     public function testUpdateThrowsExceptionIfEntityWasNotSaved()
     {
         $this->group->expects($this->once())->method('save')->will($this->throwException(new \Exception()));
         $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->group->expects($this->once())->method('getAttributeSetId')->will($this->returnValue(1));
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
         $groupDataBuilder->setName('testName');
-        $this->service->update(1, $groupDataBuilder->create());
+        $this->service->update(1, 1, $groupDataBuilder->create());
     }
 
     public function testUpdateSavesEntity()
     {
         $this->group->expects($this->once())->method('save');
         $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
-        $this->group->expects($this->once())->method('setId')->with(null);
+        $this->group->expects($this->once())->method('setId')->with(1);
+        $this->group->expects($this->once())->method('getAttributeSetId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('setAttributeGroupName')->with('testName');
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
         $groupDataBuilder->setName('testName');
-        $this->service->update(1, $groupDataBuilder->create());
+        $this->service->update(1, 1, $groupDataBuilder->create());
     }
 
     /**
@@ -141,23 +185,39 @@ class WriteServiceTest extends \PHPUnit_Framework_TestCase
         $this->group->expects($this->once())->method('getId')->will($this->returnValue(null));
         $groupDataBuilder = $this->objectHelper->getObject('Magento\Catalog\Service\V1\Data\Eav\AttributeGroupBuilder');
         $groupDataBuilder->setName('testName');
-        $this->service->delete(1, $groupDataBuilder->create());
+        $this->service->delete(1, 1);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\StateException
+     * @expectedExceptionMessage Attribute group that contains system attributes can not be deleted
      */
     public function testDeleteThrowsStateExceptionIfTryToDeleteGroupWithSystemAttributes()
     {
+        $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('hasSystemAttributes')->will($this->returnValue(true));
         $this->group->expects($this->never())->method('delete');
-        $this->service->delete(1);
+        $this->service->delete(1, 1);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\StateException
+     * @expectedExceptionMessage Attribute group does not belong to provided attribute set
+     */
+    public function testDeleteThrowsStateExceptionIfTryToDeleteGroupFromWrongAttributeSet()
+    {
+        $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->group->expects($this->once())->method('hasSystemAttributes')->will($this->returnValue(false));
+        $this->group->expects($this->once())->method('getAttributeSetId')->will($this->returnValue(0));
+        $this->group->expects($this->never())->method('delete');
+        $this->service->delete(1, 1);
     }
 
     public function testDeleteRemovesEntity()
     {
+        $this->group->expects($this->once())->method('getAttributeSetId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('getId')->will($this->returnValue(1));
         $this->group->expects($this->once())->method('delete');
-        $this->service->delete(1);
+        $this->service->delete(1, 1);
     }
 }
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/GroupPriceServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/GroupPriceServiceTest.php
new file mode 100644
index 00000000000..20f35475e10
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/GroupPriceServiceTest.php
@@ -0,0 +1,338 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+
+class GroupPriceServiceTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var GroupPriceService
+     */
+    protected $service;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $repositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceBuilderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $groupServiceMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceModifierMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $websiteMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $configMock;
+
+    protected function setUp()
+    {
+        $this->repositoryMock = $this->getMock(
+            '\Magento\Catalog\Model\ProductRepository', array(), array(), '', false
+        );
+        $this->priceBuilderMock = $this->getMock(
+            'Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder', array(), array(), '', false
+        );
+        $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+        $this->groupServiceMock = $this->getMock('\Magento\Customer\Service\V1\CustomerGroupServiceInterface');
+
+        $this->priceModifierMock =
+            $this->getMock('Magento\Catalog\Model\Product\PriceModifier', array(), array(), '', false);
+        $this->websiteMock =
+            $this->getMock('Magento\Store\Model\Website', array('getId', '__wakeup'), array(), '', false);
+        $this->productMock = $this->getMock('Magento\Catalog\Model\Product',
+            array('getData', 'setData', 'validate', 'save', 'getIdBySku', 'load', '__wakeup'), array(), '', false);
+        $this->repositoryMock->expects($this->any())->method('get')->with('product_sku')
+            ->will($this->returnValue($this->productMock));
+        $this->configMock = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface');
+        $this->service = new GroupPriceService(
+            $this->repositoryMock,
+            $this->priceBuilderMock,
+            $this->storeManagerMock,
+            $this->groupServiceMock,
+            $this->priceModifierMock,
+            $this->configMock
+        );
+    }
+
+    /**
+     * @param string $configValue
+     * @param array  $groupData
+     * @param array $expected
+     * @dataProvider getListDataProvider
+     */
+    public function testGetList($configValue, $groupData, $expected)
+    {
+        $this->repositoryMock->expects($this->once())->method('get')->with('product_sku')
+            ->will($this->returnValue($this->productMock));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array($groupData)));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue($configValue));
+        $this->priceBuilderMock
+            ->expects($this->once())
+            ->method('populateWithArray')
+            ->with($expected);
+        $this->priceBuilderMock
+            ->expects($this->once())
+            ->method('create')
+            ->will($this->returnValue('data'));
+        $prices = $this->service->getList('product_sku');
+        $this->assertCount(1, $prices);
+        $this->assertEquals('data', $prices[0]);
+    }
+
+    public function getListDataProvider()
+    {
+        return array(
+            array(
+                1,
+                array('website_price' => 10, 'price' => 5, 'all_groups' => 1),
+                array('customer_group_id' => 'all', 'value' => 10)
+            ),
+            array(
+                0,
+                array('website_price' => 10, 'price' => 5, 'all_groups' => 0, 'cust_group' => 1),
+                array('customer_group_id' => 1, 'value' => 5)
+            )
+        );
+    }
+
+    public function testSuccessDeleteGroupPrice()
+    {
+        $this->storeManagerMock
+            ->expects($this->never())
+            ->method('getWebsite');
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+        $this->priceModifierMock->expects($this->once())->method('removeGroupPrice')->with($this->productMock, 4, 0);
+
+        $this->assertEquals(true, $this->service->delete('product_sku', 4));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @message Such product doesn't exist
+     */
+    public function testDeleteGroupPriceFromNonExistingProduct()
+    {
+        $this->repositoryMock->expects($this->once())->method('get')
+            ->will($this->throwException(new NoSuchEntityException()));
+        $this->priceModifierMock->expects($this->never())->method('removeGroupPrice');
+        $this->storeManagerMock
+            ->expects($this->never())
+            ->method('getWebsite');
+        $this->service->delete('product_sku', null, 10);
+    }
+
+    public function testSuccessDeleteGroupPriceFromWebsiteLevel()
+    {
+        $this->storeManagerMock
+            ->expects($this->once())
+            ->method('getWebsite')
+            ->will($this->returnValue($this->websiteMock));
+        $this->websiteMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(1));
+        $this->priceModifierMock->expects($this->once())->method('removeGroupPrice')->with($this->productMock, 4, 1);
+
+        $this->assertEquals(true, $this->service->delete('product_sku', 4));
+    }
+
+    public function testSetNewPriceWithGlobalPriceScope()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'customer_group_id' => 1,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\GroupPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array(array('cust_group' => 2, 'website_id' => 0, 'price' => 50))));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+
+        $this->productMock->expects($this->once())->method('setData')->with(
+            'group_price',
+            array(
+                array('cust_group' => 2, 'website_id' => 0, 'price' => 50),
+                array('cust_group' => 1, 'website_id' => 0, 'price' => 100)
+            )
+        );
+        $this->productMock->expects($this->once())->method('save');
+        $this->service->set('product_sku', $price);
+    }
+
+    public function testSetUpdatedPriceWithGlobalPriceScope()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'customer_group_id' => 2,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\GroupPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array(array('cust_group' => 2, 'website_id' => 0, 'price' => 50))));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+
+        $this->productMock->expects($this->once())->method('setData')->with(
+            'group_price',
+            array(
+                array('cust_group' => 2, 'website_id' => 0, 'price' => 100),
+            )
+        );
+        $this->productMock->expects($this->once())->method('save');
+        $this->service->set('product_sku', $price);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Values of following attributes are invalid: attr1, attr2
+     */
+    public function testSetThrowsExceptionIfDoesntValidate()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+                    'customer_group_id' => 2,
+                    'value' => 100
+                )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\GroupPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array()));
+
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock->expects($this->once())->method('validate')->will($this->returnValue(
+            array('attr1' => '', 'attr2' => '')
+        ));
+        $this->productMock->expects($this->never())->method('save');
+        $this->service->set('product_sku', $price);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function testSetThrowsExceptionIfCantSave()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\GroupPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'customer_group_id' => 2,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\GroupPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('group_price')
+            ->will($this->returnValue(array()));
+
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock->expects($this->once())->method('save')->will($this->throwException(new \Exception()));
+        $this->service->set('product_sku', $price);
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/TierPriceServiceTest.php b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/TierPriceServiceTest.php
new file mode 100644
index 00000000000..476cc986eda
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Catalog/Service/V1/Product/TierPriceServiceTest.php
@@ -0,0 +1,357 @@
+<?php
+/**
+ *
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Catalog\Service\V1\Product;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+
+class TierPriceServiceTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var TierPriceService
+     */
+    protected $service;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $repositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceBuilderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $groupServiceMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceModifierMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $websiteMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $configMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    protected function setUp()
+    {
+        $this->repositoryMock = $this->getMock(
+            '\Magento\Catalog\Model\ProductRepository', array(), array(), '', false
+        );
+        $this->priceBuilderMock = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder', array(), array(), '', false
+        );
+        $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+        $this->groupServiceMock = $this->getMock('\Magento\Customer\Service\V1\CustomerGroupServiceInterface');
+        $this->websiteMock =
+            $this->getMock('Magento\Store\Model\Website', array('getId', '__wakeup'), array(), '', false);
+        $this->productMock = $this->getMock('Magento\Catalog\Model\Product',
+            array('getData', 'getIdBySku', 'load', '__wakeup', 'save', 'validate', 'setData'), array(), '', false);
+        $this->configMock = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface');
+        $this->priceModifierMock =
+            $this->getMock('Magento\Catalog\Model\Product\PriceModifier', array(), array(), '', false);
+        $this->repositoryMock->expects($this->any())->method('get')->with('product_sku')
+            ->will($this->returnValue($this->productMock));
+
+        $this->service = new TierPriceService(
+            $this->repositoryMock,
+            $this->priceBuilderMock,
+            $this->storeManagerMock,
+            $this->priceModifierMock,
+            $this->configMock,
+            $this->groupServiceMock
+        );
+    }
+
+    /**
+     * @param $configValue
+     * @param $customerGroupId
+     * @param $groupData
+     * @param $expected
+     * @dataProvider getListDataProvider
+     */
+    public function testGetList($configValue, $customerGroupId, $groupData, $expected)
+    {
+        $this->repositoryMock->expects($this->once())->method('get')->with('product_sku')
+            ->will($this->returnValue($this->productMock));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(array($groupData)));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue($configValue));
+        if ($expected) {
+            $this->priceBuilderMock
+                ->expects($this->once())
+                ->method('populateWithArray')
+                ->with($expected);
+            $this->priceBuilderMock
+                ->expects($this->once())
+                ->method('create')
+                ->will($this->returnValue('data'));
+        } else {
+            $this->priceBuilderMock->expects($this->never())->method('populateWithArray');
+        }
+        $prices = $this->service->getList('product_sku', $customerGroupId);
+        $this->assertCount($expected ? 1 : 0, $prices);
+        if ($expected) {
+            $this->assertEquals('data', $prices[0]);
+        }
+    }
+
+    public function getListDataProvider()
+    {
+        return array(
+            array(
+                1,
+                'all',
+                array('website_price' => 10, 'price' => 5, 'all_groups' => 1, 'price_qty' => 5),
+                array('value' => 10, 'qty' => 5)
+            ),
+            array(
+                0,
+                1,
+                array('website_price' => 10, 'price' => 5, 'all_groups' => 0, 'cust_group' => 1, 'price_qty' => 5),
+                array('value' => 5, 'qty' => 5)
+            ),
+            array(
+                0,
+                'all',
+                array('website_price' => 10, 'price' => 5, 'all_groups' => 0, 'cust_group' => 1, 'price_qty' => 5),
+                array()
+            )
+        );
+    }
+
+    public function testSuccessDeleteTierPrice()
+    {
+        $this->storeManagerMock
+            ->expects($this->never())
+            ->method('getWebsite');
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+        $this->priceModifierMock->expects($this->once())->method('removeTierPrice')->with($this->productMock, 4, 5, 0);
+
+        $this->assertEquals(true, $this->service->delete('product_sku', 4, 5, 0));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @message Such product doesn't exist
+     */
+    public function testDeleteTierPriceFromNonExistingProduct()
+    {
+        $this->repositoryMock->expects($this->once())->method('get')
+            ->will($this->throwException(new NoSuchEntityException()));
+        $this->priceModifierMock->expects($this->never())->method('removeTierPrice');
+        $this->storeManagerMock
+            ->expects($this->never())
+            ->method('getWebsite');
+        $this->service->delete('product_sku', null, 10, 5);
+    }
+
+    public function testSuccessDeleteTierPriceFromWebsiteLevel()
+    {
+        $this->storeManagerMock
+            ->expects($this->once())
+            ->method('getWebsite')
+            ->will($this->returnValue($this->websiteMock));
+        $this->websiteMock->expects($this->once())->method('getId')->will($this->returnValue(1));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(1));
+        $this->priceModifierMock->expects($this->once())->method('removeTierPrice')->with($this->productMock, 4, 5, 1);
+
+        $this->assertEquals(true, $this->service->delete('product_sku', 4, 5, 6));
+    }
+
+    public function testSetNewPriceWithGlobalPriceScope()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'qty' => 3,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\TierPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(
+                array(array('cust_group' => 1, 'website_id' => 0, 'price_qty' => 4, 'price' => 50))
+            ));
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+
+        $this->productMock->expects($this->once())->method('setData')->with(
+            'tier_price',
+            array(
+                array('cust_group' => 1, 'website_id' => 0, 'price_qty' => 4, 'price' => 50),
+                array('cust_group' => 1, 'website_id' => 0, 'price_qty' => 3, 'price' => 100, 'website_price' => 100)
+            )
+        );
+        $this->productMock->expects($this->once())->method('save');
+        $this->service->set('product_sku', 1, $price);
+    }
+
+    public function testSetUpdatedPriceWithGlobalPriceScope()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'qty' => 3,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\TierPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(
+                array(array('cust_group' => 1, 'website_id' => 0, 'price_qty' => 3, 'price' => 50)))
+            );
+        $this->configMock
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE)
+            ->will($this->returnValue(0));
+
+        $this->productMock->expects($this->once())->method('setData')->with(
+            'tier_price',
+            array(
+                array('cust_group' => 1, 'website_id' => 0, 'price_qty' => 3, 'price' => 100)
+            )
+        );
+        $this->productMock->expects($this->once())->method('save');
+        $this->service->set('product_sku', 1, $price);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Values of following attributes are invalid: attr1, attr2
+     */
+    public function testSetThrowsExceptionIfDoesntValidate()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'qty' => 2,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\TierPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(array()));
+
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock->expects($this->once())->method('validate')->will($this->returnValue(
+                array('attr1' => '', 'attr2' => '')
+            ));
+        $this->productMock->expects($this->never())->method('save');
+        $this->service->set('product_sku', 1, $price);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function testSetThrowsExceptionIfCantSave()
+    {
+        $priceBuilder = $this->getMock(
+            '\Magento\Catalog\Service\V1\Data\Product\TierPriceBuilder', array(), array(), '', false
+        );
+        $priceBuilder->expects($this->any())->method('getData')->will($this->returnValue(array(
+            'qty' => 2,
+            'value' => 100
+        )));
+        $price = new \Magento\Catalog\Service\V1\Data\Product\TierPrice($priceBuilder);
+        $groupBuilder = $this->getMock(
+            '\Magento\Customer\Service\V1\Data\CustomerGroupBuilder', array(), array(), '', false
+        );
+        $groupBuilder->expects($this->any())->method('getData')->will($this->returnValue(array('id' => 1)));
+        $group = new \Magento\Customer\Service\V1\Data\CustomerGroup($groupBuilder);
+        $this->productMock
+            ->expects($this->once())
+            ->method('getData')
+            ->with('tier_price')
+            ->will($this->returnValue(array()));
+
+        $this->groupServiceMock->expects($this->once())->method('getGroup')->will($this->returnValue($group));
+        $this->productMock->expects($this->once())->method('save')->will($this->throwException(new \Exception()));
+        $this->service->set('product_sku', 1, $price);
+    }
+} 
diff --git a/dev/tests/unit/testsuite/Magento/Eav/Model/Resource/Attribute/CollectionTest.php b/dev/tests/unit/testsuite/Magento/Eav/Model/Resource/Attribute/CollectionTest.php
new file mode 100644
index 00000000000..056c44d6fc5
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Eav/Model/Resource/Attribute/CollectionTest.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+namespace Magento\Eav\Model\Resource\Attribute;
+
+class CollectionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Eav\Model\Resource\Attribute\Collection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $model;
+
+    /**
+     * @var \Magento\Core\Model\EntityFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $entityFactoryMock;
+
+    /**
+     * @var \Magento\Framework\Logger|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $loggerMock;
+
+    /**
+     * @var \Magento\Framework\Data\Collection\Db\FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $fetchStrategyMock;
+
+    /**
+     * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $eventManagerMock;
+
+    /**
+     * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $eavConfigMock;
+
+    /**
+     * @var \Magento\Eav\Model\Entity\Type
+     */
+    protected $entityTypeMock;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
+    /**
+     * @var \Magento\Framework\DB\Adapter\Pdo\Mysql|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $connectionMock;
+
+    /**
+     * @var \Magento\Framework\Model\Resource\Db\AbstractDb|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $resourceMock;
+
+    /**
+     * @var \Zend_Db_Select|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $selectMock;
+
+    protected function setUp()
+    {
+        $this->entityFactoryMock = $this->getMock('Magento\Core\Model\EntityFactory', array(), array(), '', false);
+        $this->loggerMock = $this->getMock('Magento\Framework\Logger', array(), array(), '', false);
+        $this->fetchStrategyMock = $this->getMock('Magento\Framework\Data\Collection\Db\FetchStrategyInterface');
+        $this->eventManagerMock = $this->getMock('Magento\Framework\Event\ManagerInterface');
+
+        $this->eavConfigMock = $this->getMock('Magento\Eav\Model\Config', array(), array(), '', false);
+        $this->entityTypeMock = $this->getMock('Magento\Eav\Model\Entity\Type', array('__wakeup'), array(), '', false);
+        $this->entityTypeMock->setAdditionalAttributeTable('some_extra_table');
+        $this->eavConfigMock->expects($this->any())
+            ->method('getEntityType')
+            ->will($this->returnValue($this->entityTypeMock));
+
+        $this->storeManagerMock = $this->getMock('Magento\Store\Model\StoreManagerInterface');
+        $this->storeManagerMock->expects($this->any())
+            ->method('getStore')
+            ->will($this->returnSelf());
+
+        $adapter = $this->getMockForAbstractClass('Zend_Db_Adapter_Abstract', array(), '', false);
+
+        $this->selectMock = $this->getMock('Zend_Db_Select', null, array($adapter));
+
+        $this->connectionMock = $this->getMock(
+            'Magento\Framework\DB\Adapter\Pdo\Mysql',
+            array('select','describeTable', 'quoteIdentifier', '_connect', '_quote'),
+            array(),
+            '',
+            false);
+
+        $this->resourceMock = $this->getMockForAbstractClass(
+            'Magento\Framework\Model\Resource\Db\AbstractDb',
+            array(),
+            '',
+            false,
+            true,
+            true,
+            array('__wakeup', 'getReadConnection', 'getMainTable', 'getTable')
+        );
+
+        $this->connectionMock->expects($this->any())
+            ->method('select')
+            ->will($this->returnValue($this->selectMock));
+        $this->connectionMock->expects($this->any())
+            ->method('quoteIdentifier')
+            ->will($this->returnArgument(0));
+        $this->connectionMock->expects($this->any())
+            ->method('describeTable')
+            ->will($this->returnvalueMap(
+                array(
+                    array(
+                        'some_main_table',
+                        null,
+                        array(
+                            'col1' => array(),
+                            'col2' => array(),
+                        )
+                    ),
+                    array(
+                        'some_extra_table',
+                        null,
+                        array(
+                            'col2' => array(),
+                            'col3' => array(),
+                        )
+                    ),
+                    array(
+                        null,
+                        null,
+                        array(
+                            'col2' => array(),
+                            'col3' => array(),
+                            'col4' => array(),
+                        )
+                    ),
+                )
+            ));
+        $this->connectionMock->expects($this->any())
+            ->method('_quote')
+            ->will($this->returnArgument(0));
+
+        $this->resourceMock->expects($this->any())
+            ->method('getReadConnection')
+            ->will($this->returnValue($this->connectionMock));
+        $this->resourceMock->expects($this->any())
+            ->method('getMainTable')
+            ->will($this->returnValue('some_main_table'));
+        $this->resourceMock->expects($this->any())
+            ->method('getTable')
+            ->will(
+                $this->returnValue('some_extra_table')
+            );
+    }
+
+    /**
+     * Test that Magento\Eav\Model\Resource\Attribute\Collection::_initSelect sets expressions
+     * that can be properly quoted by Zend_Db_Expr::quoteIdentifier
+     *
+     * @dataProvider initSelectDataProvider
+     */
+    public function testInitSelect($column, $value, $expected)
+    {
+        $helper = new \Magento\TestFramework\Helper\ObjectManager($this);
+        $this->model = $helper->getObject('\Magento\Customer\Model\Resource\Attribute\Collection',
+            array(
+                'entityFactory' => $this->entityFactoryMock,
+                'logger' => $this->loggerMock,
+                'fetchStrategy' => $this->fetchStrategyMock,
+                'eventManager' => $this->eventManagerMock,
+                'eavConfig' => $this->eavConfigMock,
+                'storeManager' => $this->storeManagerMock,
+                'connection' => $this->connectionMock,
+                'resource' => $this->resourceMock
+            )
+        );
+
+        $this->model->addFieldToFilter($column, $value);
+        $this->assertEquals($expected, $this->model->getSelectCountSql()->assemble());
+    }
+
+    public function initSelectDataProvider()
+    {
+        return array(
+            'main_table_expression' => array(
+                'col2', '1',
+                'SELECT COUNT(*) FROM "some_main_table" AS "main_table"' . "\n"
+                . ' INNER JOIN "some_extra_table" AS "additional_table"'
+                . ' ON additional_table.attribute_id = main_table.attribute_id' . "\n"
+                . ' LEFT JOIN "some_extra_table" AS "scope_table"'
+                . ' ON scope_table.attribute_id = main_table.attribute_id'
+                . ' AND scope_table.website_id = :scope_website_id'
+                . ' WHERE (main_table.entity_type_id = :mt_entity_type_id)'
+                . ' AND (IF(main_table.col2 IS NULL, scope_table.col2, main_table.col2) = 1)'
+            ),
+            'additional_table_expression' => array(
+                'col3', '2',
+                'SELECT COUNT(*) FROM "some_main_table" AS "main_table"' . "\n"
+                . ' INNER JOIN "some_extra_table" AS "additional_table"'
+                . ' ON additional_table.attribute_id = main_table.attribute_id'. "\n"
+                . ' LEFT JOIN "some_extra_table" AS "scope_table"'
+                . ' ON scope_table.attribute_id = main_table.attribute_id'
+                . ' AND scope_table.website_id = :scope_website_id'
+                . ' WHERE (main_table.entity_type_id = :mt_entity_type_id)'
+                . ' AND (IF(additional_table.col3 IS NULL, scope_table.col3, additional_table.col3) = 2)'
+            )
+        );
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/SalesRule/Model/ValidatorTest.php b/dev/tests/unit/testsuite/Magento/SalesRule/Model/ValidatorTest.php
index dbd6d6b4e23..3d8e21bf9d0 100644
--- a/dev/tests/unit/testsuite/Magento/SalesRule/Model/ValidatorTest.php
+++ b/dev/tests/unit/testsuite/Magento/SalesRule/Model/ValidatorTest.php
@@ -32,6 +32,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
 
     protected function setUp()
     {
+        // @TODO Re-write test according to standards of writing test (e.g do not mock tested class)
         $this->model = $this->getMock(
             'Magento\SalesRule\Model\Validator',
             array('_getRules', '_getItemOriginalPrice', '_getItemBaseOriginalPrice', '__wakeup'),
@@ -97,8 +98,6 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
         $quote->setVirtualItemsQty(2);
 
         $this->assertTrue($this->model->canApplyRules($item));
-
-        return true;
     }
 
     public function testProcess()
@@ -449,4 +448,47 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
 
         return $validator;
     }
+
+    public function testInit()
+    {
+        $websiteId = 1;
+        $customerGroupId = 2;
+        $couponCode = 'code';
+
+        $ruleCollection = $this->getMockBuilder('Magento\SalesRule\Model\Resource\Rule\Collection')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $ruleCollection->expects($this->once())
+            ->method('setValidationFilter')
+            ->with($websiteId, $customerGroupId, $couponCode)
+            ->will($this->returnSelf());
+        $ruleCollection->expects($this->once())
+            ->method('addFieldToFilter')
+            ->with('is_active', 1)
+            ->will($this->returnSelf());
+        $ruleCollection->expects($this->once())
+            ->method('load')
+            ->will($this->returnSelf());
+
+        $ruleCollectionFactoryMock = $this->getMockBuilder('Magento\SalesRule\Model\Resource\Rule\CollectionFactory')
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $ruleCollectionFactoryMock->expects($this->once())
+            ->method('create')
+            ->will($this->returnValue($ruleCollection));
+
+        $helper = new \Magento\TestFramework\Helper\ObjectManager($this);
+        $model = $helper->getObject(
+            'Magento\SalesRule\Model\Validator',
+            [
+                'collectionFactory' => $ruleCollectionFactoryMock
+            ]
+        );
+
+        $this->assertInstanceOf(
+            'Magento\SalesRule\Model\Validator',
+            $model->init($websiteId, $customerGroupId, $couponCode)
+        );
+    }
 }
diff --git a/dev/tests/unit/testsuite/Magento/Tax/Model/ConfigTest.php b/dev/tests/unit/testsuite/Magento/Tax/Model/ConfigTest.php
new file mode 100644
index 00000000000..3502632adeb
--- /dev/null
+++ b/dev/tests/unit/testsuite/Magento/Tax/Model/ConfigTest.php
@@ -0,0 +1,405 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @copyright   Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
+ * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
+ */
+
+/**
+ * Test class for \Magento\Tax\Model\Config
+ */
+namespace Magento\Tax\Model;
+
+use Magento\Tax\Model\Config;
+
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests the setter/getter methods that bypass the ScopeConfigInterface object
+     *
+     * @param string $setterMethod
+     * @param string $getterMethod
+     * @param bool $value
+     * @dataProvider dataProviderDirectSettersGettersMethods
+     */
+    public function testDirectSettersGettersMethods($setterMethod, $getterMethod, $value)
+    {
+        // Need a mocked object with only dummy methods.  It is just needed for construction.
+        // The setter/getter methods do not use this object (for this set of tests).
+        $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface');
+
+        /** @var \Magento\Tax\Model\Config */
+        $model = new Config($scopeConfigMock);
+        $model->{$setterMethod}($value);
+        $this->assertEquals($value, $model->{$getterMethod}());
+    }
+
+    /**
+     * Returns a set of 'true' and 'false' parameters for each of the setter/getter method pairs
+     *
+     * @return array
+     */
+    public function dataProviderDirectSettersGettersMethods()
+    {
+        return $this->_buildTrueFalsePairs(
+            [
+                [
+                    'setShippingPriceIncludeTax',
+                    'shippingPriceIncludesTax'
+                ],
+                [
+                    'setNeedUseShippingExcludeTax',
+                    'getNeedUseShippingExcludeTax'
+                ],
+                [
+                    'setPriceIncludesTax',
+                    'priceIncludesTax'
+                ]
+            ]
+        );
+    }
+
+    /**
+     * Returns an output array that is twice the size of the input array by adding 'true' and then 'false' to the
+     * set of parameters given
+     *
+     * @param array $arrayIn
+     * @return array
+     */
+    protected function _buildTrueFalsePairs($arrayIn)
+    {
+        $arrayOut = [];
+
+        foreach ($arrayIn as $paramArray) {
+            // replicate the paramArray, append 'true', and add the new array to the output array
+            $arrayT = $paramArray;
+            $arrayT[] = true;
+            $arrayOut[] = $arrayT;
+            // replicate the paramArray, append 'false', and add the new array to the output array
+            $arrayF = $paramArray;
+            $arrayF[] = false;
+            $arrayOut[] = $arrayF;
+        }
+
+        return $arrayOut;
+    }
+
+
+    /**
+     * Tests the getCalculationSequence method
+     *
+     * @param bool $applyTaxAfterDiscount
+     * @param bool $discountTaxIncl
+     * @param string $expectedValue
+     * @dataProvider dataProviderGetCalculationSequence
+     */
+    public function testGetCalculationSequence($applyTaxAfterDiscount, $discountTaxIncl, $expectedValue)
+    {
+        $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface');
+        $scopeConfigMock->expects(
+            $this->at(0))->method('getValue')->will($this->returnValue($applyTaxAfterDiscount));
+        $scopeConfigMock->expects(
+            $this->at(1))->method('getValue')->will($this->returnValue($discountTaxIncl));
+
+        /** @var \Magento\Tax\Model\Config */
+        $model = new Config($scopeConfigMock);
+        $this->assertEquals($expectedValue, $model->getCalculationSequence());
+    }
+
+    /**
+     * @return array
+     */
+    public function dataProviderGetCalculationSequence()
+    {
+        return [
+            [true,  true,  \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL],
+            [true,  false, \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL],
+            [false, true,  \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL],
+            [false, false, \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL]
+        ];
+    }
+
+
+    /**
+     * Tests the methods that rely on the ScopeConfigInterface object to provide their return values
+     *
+     * @param string $method
+     * @param string $path
+     * @param bool|int $configValue
+     * @param bool $expectedValue
+     * @dataProvider dataProviderScopeConfigMethods
+     */
+    public function testScopeConfigMethods($method, $path, $configValue, $expectedValue)
+    {
+        $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface');
+        $scopeConfigMock->expects($this->once())
+            ->method('getValue')
+            ->with($path, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, null)
+            ->will($this->returnValue($configValue));
+
+        /** @var \Magento\Tax\Model\Config */
+        $model = new Config($scopeConfigMock);
+        $this->assertEquals($expectedValue, $model->{$method}());
+    }
+
+    /**
+     * @return array
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function dataProviderScopeConfigMethods()
+    {
+        return [
+            [
+                'priceIncludesTax',
+                Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX,
+                true,
+                true
+            ],
+            [
+                'applyTaxAfterDiscount',
+                Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT,
+                true,
+                true
+            ],
+            [
+                'getPriceDisplayType',
+                Config::CONFIG_XML_PATH_PRICE_DISPLAY_TYPE,
+                true,
+                true
+            ],
+            [
+                'discountTax',
+                Config::CONFIG_XML_PATH_DISCOUNT_TAX,
+                1,
+                true
+            ],
+            [
+                'getAlgorithm',
+                Config::XML_PATH_ALGORITHM,
+                true,
+                true
+            ],
+            [
+                'getShippingTaxClass',
+                Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS,
+                true,
+                true
+            ],
+            [
+                'getShippingPriceDisplayType',
+                Config::CONFIG_XML_PATH_DISPLAY_SHIPPING,
+                true,
+                true
+            ],
+            [
+                'shippingPriceIncludesTax',
+                Config::CONFIG_XML_PATH_SHIPPING_INCLUDES_TAX,
+                true,
+                true
+            ],
+            [
+                'displayCartPricesInclTax',
+                Config::XML_PATH_DISPLAY_CART_PRICE,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartPricesExclTax',
+                Config::XML_PATH_DISPLAY_CART_PRICE,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartPricesBoth',
+                Config::XML_PATH_DISPLAY_CART_PRICE,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displayCartSubtotalInclTax',
+                Config::XML_PATH_DISPLAY_CART_SUBTOTAL,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartSubtotalExclTax',
+                Config::XML_PATH_DISPLAY_CART_SUBTOTAL,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartSubtotalBoth',
+                Config::XML_PATH_DISPLAY_CART_SUBTOTAL,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displayCartShippingInclTax',
+                Config::XML_PATH_DISPLAY_CART_SHIPPING,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartShippingExclTax',
+                Config::XML_PATH_DISPLAY_CART_SHIPPING,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartShippingBoth',
+                Config::XML_PATH_DISPLAY_CART_SHIPPING,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displayCartDiscountInclTax',
+                Config::XML_PATH_DISPLAY_CART_DISCOUNT,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartDiscountExclTax',
+                Config::XML_PATH_DISPLAY_CART_DISCOUNT,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displayCartDiscountBoth',
+                Config::XML_PATH_DISPLAY_CART_DISCOUNT,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displayCartTaxWithGrandTotal',
+                Config::XML_PATH_DISPLAY_CART_GRANDTOTAL,
+                true,
+                true
+            ],
+            [
+                'displayCartFullSummary',
+                Config::XML_PATH_DISPLAY_CART_FULL_SUMMARY,
+                true,
+                true
+            ],
+            [
+                'displayCartZeroTax',
+                Config::XML_PATH_DISPLAY_CART_ZERO_TAX,
+                true,
+                true
+            ],
+            [
+                'displaySalesPricesInclTax',
+                Config::XML_PATH_DISPLAY_SALES_PRICE,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesPricesExclTax',
+                Config::XML_PATH_DISPLAY_SALES_PRICE,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesPricesBoth',
+                Config::XML_PATH_DISPLAY_SALES_PRICE,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displaySalesSubtotalInclTax',
+                Config::XML_PATH_DISPLAY_SALES_SUBTOTAL,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesSubtotalExclTax',
+                Config::XML_PATH_DISPLAY_SALES_SUBTOTAL,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesSubtotalBoth',
+                Config::XML_PATH_DISPLAY_SALES_SUBTOTAL,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displaySalesShippingInclTax',
+                Config::XML_PATH_DISPLAY_SALES_SHIPPING,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesShippingExclTax',
+                Config::XML_PATH_DISPLAY_SALES_SHIPPING,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesShippingBoth',
+                Config::XML_PATH_DISPLAY_SALES_SHIPPING,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displaySalesDiscountInclTax',
+                Config::XML_PATH_DISPLAY_SALES_DISCOUNT,
+                Config::DISPLAY_TYPE_INCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesDiscountExclTax',
+                Config::XML_PATH_DISPLAY_SALES_DISCOUNT,
+                Config::DISPLAY_TYPE_EXCLUDING_TAX,
+                true
+            ],
+            [
+                'displaySalesDiscountBoth',
+                Config::XML_PATH_DISPLAY_SALES_DISCOUNT,
+                Config::DISPLAY_TYPE_BOTH,
+                true
+            ],
+            [
+                'displaySalesTaxWithGrandTotal',
+                Config::XML_PATH_DISPLAY_SALES_GRANDTOTAL,
+                true,
+                true
+            ],
+            [
+                'displaySalesFullSummary',
+                Config::XML_PATH_DISPLAY_SALES_FULL_SUMMARY,
+                true,
+                true
+            ],
+            [
+                'displaySalesZeroTax',
+                Config::XML_PATH_DISPLAY_SALES_ZERO_TAX,
+                true,
+                true
+            ],
+            [
+                'crossBorderTradeEnabled',
+                Config::CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED,
+                true,
+                true
+            ]
+        ];
+    }
+}
diff --git a/dev/tests/unit/testsuite/Magento/Tax/Pricing/AdjustmentTest.php b/dev/tests/unit/testsuite/Magento/Tax/Pricing/AdjustmentTest.php
index 74ea74b8a90..ed4708b27ca 100644
--- a/dev/tests/unit/testsuite/Magento/Tax/Pricing/AdjustmentTest.php
+++ b/dev/tests/unit/testsuite/Magento/Tax/Pricing/AdjustmentTest.php
@@ -119,6 +119,10 @@ class AdjustmentTest extends \PHPUnit_Framework_TestCase
             ->method('getPrice')
             ->with($object, $amount)
             ->will($this->returnValue($price));
+        $this->taxHelper->expects($this->any())
+            ->method('getPriceUnrounded')
+            ->with($object, $amount)
+            ->will($this->returnValue($price));
 
         $this->assertEquals($expectedResult, $this->adjustment->extractAdjustment($amount, $object));
     }
@@ -151,6 +155,10 @@ class AdjustmentTest extends \PHPUnit_Framework_TestCase
             ->method('getPrice')
             ->with($object, $amount, !$isPriceIncludesTax)
             ->will($this->returnValue($price));
+        $this->taxHelper->expects($this->any())
+            ->method('getPriceUnrounded')
+            ->with($object, $amount, !$isPriceIncludesTax)
+            ->will($this->returnValue($price));
 
         $this->assertEquals($expectedResult, $this->adjustment->applyAdjustment($amount, $object));
     }
diff --git a/lib/internal/Magento/Framework/AppInterface.php b/lib/internal/Magento/Framework/AppInterface.php
index 3800dabc2ff..0c82cf557f8 100644
--- a/lib/internal/Magento/Framework/AppInterface.php
+++ b/lib/internal/Magento/Framework/AppInterface.php
@@ -35,7 +35,7 @@ interface AppInterface
     /**
      * Magento version
      */
-    const VERSION = '2.0.0.0-dev82';
+    const VERSION = '2.0.0.0-dev83';
 
     /**
      * Launch application
-- 
GitLab