[REFACTOR] Move and refactor tests.

Some tests from boot_control_android_unittest only
test logic of UpdatePartitionMetadata. Move them to
dynamic_partition_control_android_unittest.

After moving the test, boot_control_android_unittest
can depend on the MockDynamicPartitionControl (which
is a mocked DynamicPartitionControlInterface) directly
again. It no longer depends on internal implementation
of DynamicPartitionControlAndroid.

Test: unittest
Fixes: 138333673

Change-Id: Idc5f11be98754b8f6d38fcb8604af497e5d86376
diff --git a/Android.bp b/Android.bp
index fab5d27..57b2feb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -678,6 +678,7 @@
         "common/terminator_unittest.cc",
         "common/test_utils.cc",
         "common/utils_unittest.cc",
+        "dynamic_partition_control_android_unittest.cc",
         "payload_consumer/bzip_extent_writer_unittest.cc",
         "payload_consumer/cached_file_descriptor_unittest.cc",
         "payload_consumer/delta_performer_integration_test.cc",
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
index 3e0320d..1a87547 100644
--- a/boot_control_android_unittest.cc
+++ b/boot_control_android_unittest.cc
@@ -26,243 +26,29 @@
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 
+#include "update_engine/dynamic_partition_test_utils.h"
 #include "update_engine/mock_boot_control_hal.h"
 #include "update_engine/mock_dynamic_partition_control.h"
 
 using android::dm::DmDeviceState;
-using android::fs_mgr::MetadataBuilder;
 using android::hardware::Void;
 using std::string;
 using testing::_;
 using testing::AnyNumber;
-using testing::Contains;
-using testing::Eq;
 using testing::Invoke;
-using testing::Key;
-using testing::MakeMatcher;
-using testing::Matcher;
-using testing::MatcherInterface;
-using testing::MatchResultListener;
 using testing::NiceMock;
 using testing::Not;
 using testing::Return;
 
 namespace chromeos_update_engine {
 
-constexpr const uint32_t kMaxNumSlots = 2;
-constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
-constexpr const char* kFakeDevicePath = "/fake/dev/path/";
-constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
-constexpr const uint32_t kFakeMetadataSize = 65536;
-constexpr const char* kDefaultGroup = "foo";
-
-// A map describing the size of each partition.
-// "{name, size}"
-using PartitionSizes = std::map<string, uint64_t>;
-
-// "{name_a, size}"
-using PartitionSuffixSizes = std::map<string, uint64_t>;
-
-using PartitionMetadata = BootControlInterface::PartitionMetadata;
-
-// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
-// of user-defined literal operators.
-constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
-  return x << 20;
-}
-constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
-  return x << 30;
-}
-
-constexpr uint64_t kDefaultGroupSize = 5_GiB;
-// Super device size. 1 MiB for metadata.
-constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
-
-template <typename U, typename V>
-std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
-  os << "{";
-  bool first = true;
-  for (const auto& pair : param) {
-    if (!first)
-      os << ", ";
-    os << pair.first << ":" << pair.second;
-    first = false;
-  }
-  return os << "}";
-}
-
-template <typename T>
-std::ostream& operator<<(std::ostream& os, const std::vector<T>& param) {
-  os << "[";
-  bool first = true;
-  for (const auto& e : param) {
-    if (!first)
-      os << ", ";
-    os << e;
-    first = false;
-  }
-  return os << "]";
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const PartitionMetadata::Partition& p) {
-  return os << "{" << p.name << ", " << p.size << "}";
-}
-
-std::ostream& operator<<(std::ostream& os, const PartitionMetadata::Group& g) {
-  return os << "{" << g.name << ", " << g.size << ", " << g.partitions << "}";
-}
-
-std::ostream& operator<<(std::ostream& os, const PartitionMetadata& m) {
-  return os << m.groups;
-}
-
-inline string GetDevice(const string& name) {
-  return kFakeDevicePath + name;
-}
-
-inline string GetDmDevice(const string& name) {
-  return kFakeDmDevicePath + name;
-}
-
-// TODO(elsk): fs_mgr_get_super_partition_name should be mocked.
-inline string GetSuperDevice(uint32_t slot) {
-  return GetDevice(fs_mgr_get_super_partition_name(slot));
-}
-
-struct TestParam {
-  uint32_t source;
-  uint32_t target;
-};
-std::ostream& operator<<(std::ostream& os, const TestParam& param) {
-  return os << "{source: " << param.source << ", target:" << param.target
-            << "}";
-}
-
-// To support legacy tests, auto-convert {name_a: size} map to
-// PartitionMetadata.
-PartitionMetadata partitionSuffixSizesToMetadata(
-    const PartitionSuffixSizes& partition_sizes) {
-  PartitionMetadata metadata;
-  for (const char* suffix : kSlotSuffixes) {
-    metadata.groups.push_back(
-        {string(kDefaultGroup) + suffix, kDefaultGroupSize, {}});
-  }
-  for (const auto& pair : partition_sizes) {
-    for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
-      if (base::EndsWith(pair.first,
-                         kSlotSuffixes[suffix_idx],
-                         base::CompareCase::SENSITIVE)) {
-        metadata.groups[suffix_idx].partitions.push_back(
-            {pair.first, pair.second});
-      }
-    }
-  }
-  return metadata;
-}
-
-// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
-PartitionMetadata partitionSizesToMetadata(
-    const PartitionSizes& partition_sizes) {
-  PartitionMetadata metadata;
-  metadata.groups.push_back({string{kDefaultGroup}, kDefaultGroupSize, {}});
-  for (const auto& pair : partition_sizes) {
-    metadata.groups[0].partitions.push_back({pair.first, pair.second});
-  }
-  return metadata;
-}
-
-std::unique_ptr<MetadataBuilder> NewFakeMetadata(
-    const PartitionMetadata& metadata) {
-  auto builder =
-      MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
-  EXPECT_GE(builder->AllocatableSpace(), kDefaultGroupSize * 2);
-  EXPECT_NE(nullptr, builder);
-  if (builder == nullptr)
-    return nullptr;
-  for (const auto& group : metadata.groups) {
-    EXPECT_TRUE(builder->AddGroup(group.name, group.size));
-    for (const auto& partition : group.partitions) {
-      auto p = builder->AddPartition(partition.name, group.name, 0 /* attr */);
-      EXPECT_TRUE(p && builder->ResizePartition(p, partition.size));
-    }
-  }
-  return builder;
-}
-
-class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
- public:
-  explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
-      : partition_metadata_(partitionSuffixSizesToMetadata(partition_sizes)) {}
-  explicit MetadataMatcher(const PartitionMetadata& partition_metadata)
-      : partition_metadata_(partition_metadata) {}
-
-  bool MatchAndExplain(MetadataBuilder* metadata,
-                       MatchResultListener* listener) const override {
-    bool success = true;
-    for (const auto& group : partition_metadata_.groups) {
-      for (const auto& partition : group.partitions) {
-        auto p = metadata->FindPartition(partition.name);
-        if (p == nullptr) {
-          if (!success)
-            *listener << "; ";
-          *listener << "No partition " << partition.name;
-          success = false;
-          continue;
-        }
-        if (p->size() != partition.size) {
-          if (!success)
-            *listener << "; ";
-          *listener << "Partition " << partition.name << " has size "
-                    << p->size() << ", expected " << partition.size;
-          success = false;
-        }
-        if (p->group_name() != group.name) {
-          if (!success)
-            *listener << "; ";
-          *listener << "Partition " << partition.name << " has group "
-                    << p->group_name() << ", expected " << group.name;
-          success = false;
-        }
-      }
-    }
-    return success;
-  }
-
-  void DescribeTo(std::ostream* os) const override {
-    *os << "expect: " << partition_metadata_;
-  }
-
-  void DescribeNegationTo(std::ostream* os) const override {
-    *os << "expect not: " << partition_metadata_;
-  }
-
- private:
-  PartitionMetadata partition_metadata_;
-};
-
-inline Matcher<MetadataBuilder*> MetadataMatches(
-    const PartitionSuffixSizes& partition_sizes) {
-  return MakeMatcher(new MetadataMatcher(partition_sizes));
-}
-
-inline Matcher<MetadataBuilder*> MetadataMatches(
-    const PartitionMetadata& partition_metadata) {
-  return MakeMatcher(new MetadataMatcher(partition_metadata));
-}
-
-MATCHER_P(HasGroup, group, " has group " + group) {
-  auto groups = arg->ListGroups();
-  return std::find(groups.begin(), groups.end(), group) != groups.end();
-}
-
 class BootControlAndroidTest : public ::testing::Test {
  protected:
   void SetUp() override {
     // Fake init bootctl_
     bootctl_.module_ = new NiceMock<MockBootControlHal>();
     bootctl_.dynamic_control_ =
-        std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
+        std::make_unique<NiceMock<MockDynamicPartitionControl>>();
 
     ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] {
       return kMaxNumSlots;
@@ -295,61 +81,22 @@
   }
 
   // Return the mocked DynamicPartitionControlInterface.
-  NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
-    return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(
+  NiceMock<MockDynamicPartitionControl>& dynamicControl() {
+    return static_cast<NiceMock<MockDynamicPartitionControl>&>(
         *bootctl_.dynamic_control_);
   }
 
   // Set the fake metadata to return when LoadMetadataBuilder is called on
   // |slot|.
   void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
-    SetMetadata(slot, partitionSuffixSizesToMetadata(sizes));
-  }
-
-  void SetMetadata(uint32_t slot, const PartitionMetadata& metadata) {
     EXPECT_CALL(dynamicControl(),
-                LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+                LoadMetadataBuilder(GetSuperDevice(slot), slot))
         .Times(AnyNumber())
-        .WillRepeatedly(Invoke([metadata](auto, auto, auto) {
-          return NewFakeMetadata(metadata);
+        .WillRepeatedly(Invoke([sizes](auto, auto) {
+          return NewFakeMetadata(PartitionSuffixSizesToMetadata(sizes));
         }));
   }
 
-  // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
-  // slot with each partition in |partitions|.
-  void ExpectUnmap(const std::set<string>& partitions) {
-    // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
-    ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
-        .WillByDefault(Return(false));
-
-    for (const auto& partition : partitions) {
-      EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
-          .WillOnce(Invoke([this](auto partition) {
-            mapped_devices_.erase(partition);
-            return true;
-          }));
-    }
-  }
-
-  void ExpectDevicesAreMapped(const std::set<string>& partitions) {
-    ASSERT_EQ(partitions.size(), mapped_devices_.size());
-    for (const auto& partition : partitions) {
-      EXPECT_THAT(mapped_devices_, Contains(Key(Eq(partition))))
-          << "Expect that " << partition << " is mapped, but it is not.";
-    }
-  }
-
-  void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
-    ExpectStoreMetadataMatch(MetadataMatches(partition_sizes));
-  }
-
-  virtual void ExpectStoreMetadataMatch(
-      const Matcher<MetadataBuilder*>& matcher) {
-    EXPECT_CALL(dynamicControl(),
-                StoreMetadata(GetSuperDevice(target()), matcher, target()))
-        .WillOnce(Return(true));
-  }
-
   uint32_t source() { return slots_.source; }
 
   uint32_t target() { return slots_.target; }
@@ -367,28 +114,17 @@
     ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] {
       return source();
     }));
-    // Should not store metadata to source slot.
-    EXPECT_CALL(dynamicControl(),
-                StoreMetadata(GetSuperDevice(source()), _, source()))
-        .Times(0);
-    // Should not load metadata from target slot.
-    EXPECT_CALL(dynamicControl(),
-                LoadMetadataBuilder(GetSuperDevice(target()), target(), _))
-        .Times(0);
   }
 
   bool InitPartitionMetadata(uint32_t slot,
                              PartitionSizes partition_sizes,
                              bool update_metadata = true) {
-    auto m = partitionSizesToMetadata(partition_sizes);
-    LOG(INFO) << m;
+    auto m = PartitionSizesToMetadata(partition_sizes);
     return bootctl_.InitPartitionMetadata(slot, m, update_metadata);
   }
 
   BootControlAndroid bootctl_;  // BootControlAndroid under test.
   TestParam slots_;
-  // mapped devices through MapPartitionOnDeviceMapper.
-  std::map<string, string> mapped_devices_;
 };
 
 class BootControlAndroidTestP
@@ -401,125 +137,6 @@
   }
 };
 
-// Test resize case. Grow if target metadata contains a partition with a size
-// less than expected.
-TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) {
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-  ExpectStoreMetadata({{S("system"), 2_GiB},
-                       {S("vendor"), 1_GiB},
-                       {T("system"), 3_GiB},
-                       {T("vendor"), 1_GiB}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(
-      InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test resize case. Shrink if target metadata contains a partition with a size
-// greater than expected.
-TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) {
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-  ExpectStoreMetadata({{S("system"), 2_GiB},
-                       {S("vendor"), 1_GiB},
-                       {T("system"), 2_GiB},
-                       {T("vendor"), 150_MiB}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(InitPartitionMetadata(target(),
-                                    {{"system", 2_GiB}, {"vendor", 150_MiB}}));
-}
-
-// Test adding partitions on the first run.
-TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) {
-  SetMetadata(source(), PartitionSuffixSizes{});
-  ExpectStoreMetadata({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(
-      InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test subsequent add case.
-TEST_P(BootControlAndroidTestP, AddAdditionalPartition) {
-  SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
-  ExpectStoreMetadata(
-      {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(
-      InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test delete one partition.
-TEST_P(BootControlAndroidTestP, DeletePartition) {
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-  // No T("vendor")
-  ExpectStoreMetadata(
-      {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}});
-  ExpectUnmap({T("system")});
-
-  EXPECT_TRUE(InitPartitionMetadata(target(), {{"system", 2_GiB}}));
-}
-
-// Test delete all partitions.
-TEST_P(BootControlAndroidTestP, DeleteAll) {
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-  ExpectStoreMetadata({{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
-
-  EXPECT_TRUE(InitPartitionMetadata(target(), {}));
-}
-
-// Test corrupt source metadata case.
-TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
-  EXPECT_CALL(dynamicControl(),
-              LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
-      .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
-  ExpectUnmap({T("system")});
-
-  EXPECT_FALSE(InitPartitionMetadata(target(), {{"system", 1_GiB}}))
-      << "Should not be able to continue with corrupt source metadata";
-}
-
-// Test that InitPartitionMetadata fail if there is not enough space on the
-// device.
-TEST_P(BootControlAndroidTestP, NotEnoughSpace) {
-  SetMetadata(source(),
-              {{S("system"), 3_GiB},
-               {S("vendor"), 2_GiB},
-               {T("system"), 0},
-               {T("vendor"), 0}});
-  EXPECT_FALSE(
-      InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
-      << "Should not be able to fit 11GiB data into 10GiB space";
-}
-
-TEST_P(BootControlAndroidTestP, NotEnoughSpaceForSlot) {
-  SetMetadata(source(),
-              {{S("system"), 1_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 0},
-               {T("vendor"), 0}});
-  EXPECT_FALSE(
-      InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
-      << "Should not be able to grow over size of super / 2";
-}
-
 // Test applying retrofit update on a build with dynamic partitions enabled.
 TEST_P(BootControlAndroidTestP,
        ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
@@ -528,12 +145,6 @@
                {S("vendor"), 1_GiB},
                {T("system"), 2_GiB},
                {T("vendor"), 1_GiB}});
-  // Should not try to unmap any target partition.
-  EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_)).Times(0);
-  // Should not store metadata to target slot.
-  EXPECT_CALL(dynamicControl(),
-              StoreMetadata(GetSuperDevice(target()), _, target()))
-      .Times(0);
 
   // Not calling through BootControlAndroidTest::InitPartitionMetadata(), since
   // we don't want any default group in the PartitionMetadata.
@@ -576,9 +187,7 @@
                {S("vendor"), 1_GiB},
                {T("system"), 2_GiB},
                {T("vendor"), 1_GiB}});
-  EXPECT_CALL(dynamicControl(),
-              StoreMetadata(GetSuperDevice(target()), _, target()))
-      .Times(0);
+
   EXPECT_TRUE(InitPartitionMetadata(
       target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}, false));
 
@@ -620,239 +229,10 @@
                         BootControlAndroidTestP,
                         testing::Values(TestParam{0, 1}, TestParam{1, 0}));
 
-const PartitionSuffixSizes update_sizes_0() {
-  // Initial state is 0 for "other" slot.
-  return {
-      {"grown_a", 2_GiB},
-      {"shrunk_a", 1_GiB},
-      {"same_a", 100_MiB},
-      {"deleted_a", 150_MiB},
-      // no added_a
-      {"grown_b", 200_MiB},
-      // simulate system_other
-      {"shrunk_b", 0},
-      {"same_b", 0},
-      {"deleted_b", 0},
-      // no added_b
-  };
-}
-
-const PartitionSuffixSizes update_sizes_1() {
-  return {
-      {"grown_a", 2_GiB},
-      {"shrunk_a", 1_GiB},
-      {"same_a", 100_MiB},
-      {"deleted_a", 150_MiB},
-      // no added_a
-      {"grown_b", 3_GiB},
-      {"shrunk_b", 150_MiB},
-      {"same_b", 100_MiB},
-      {"added_b", 150_MiB},
-      // no deleted_b
-  };
-}
-
-const PartitionSuffixSizes update_sizes_2() {
-  return {
-      {"grown_a", 4_GiB},
-      {"shrunk_a", 100_MiB},
-      {"same_a", 100_MiB},
-      {"deleted_a", 64_MiB},
-      // no added_a
-      {"grown_b", 3_GiB},
-      {"shrunk_b", 150_MiB},
-      {"same_b", 100_MiB},
-      {"added_b", 150_MiB},
-      // no deleted_b
-  };
-}
-
-// Test case for first update after the device is manufactured, in which
-// case the "other" slot is likely of size "0" (except system, which is
-// non-zero because of system_other partition)
-TEST_F(BootControlAndroidTest, SimulatedFirstUpdate) {
-  SetSlots({0, 1});
-
-  SetMetadata(source(), update_sizes_0());
-  SetMetadata(target(), update_sizes_0());
-  ExpectStoreMetadata(update_sizes_1());
-  ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
-
-  EXPECT_TRUE(InitPartitionMetadata(target(),
-                                    {{"grown", 3_GiB},
-                                     {"shrunk", 150_MiB},
-                                     {"same", 100_MiB},
-                                     {"added", 150_MiB}}));
-}
-
-// After first update, test for the second update. In the second update, the
-// "added" partition is deleted and "deleted" partition is re-added.
-TEST_F(BootControlAndroidTest, SimulatedSecondUpdate) {
-  SetSlots({1, 0});
-
-  SetMetadata(source(), update_sizes_1());
-  SetMetadata(target(), update_sizes_0());
-
-  ExpectStoreMetadata(update_sizes_2());
-  ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
-
-  EXPECT_TRUE(InitPartitionMetadata(target(),
-                                    {{"grown", 4_GiB},
-                                     {"shrunk", 100_MiB},
-                                     {"same", 100_MiB},
-                                     {"deleted", 64_MiB}}));
-}
-
 TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
   SetSlots({1, 1});
   EXPECT_FALSE(InitPartitionMetadata(target(), {}))
       << "Should not be able to apply to current slot.";
 }
 
-class BootControlAndroidGroupTestP : public BootControlAndroidTestP {
- public:
-  void SetUp() override {
-    BootControlAndroidTestP::SetUp();
-    SetMetadata(
-        source(),
-        {.groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
-                    SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB),
-                    SimpleGroup(T("android"), 3_GiB, T("system"), 0),
-                    SimpleGroup(T("oem"), 2_GiB, T("vendor"), 0)}});
-  }
-
-  // Return a simple group with only one partition.
-  PartitionMetadata::Group SimpleGroup(const string& group,
-                                       uint64_t group_size,
-                                       const string& partition,
-                                       uint64_t partition_size) {
-    return {.name = group,
-            .size = group_size,
-            .partitions = {{.name = partition, .size = partition_size}}};
-  }
-
-  void ExpectStoreMetadata(const PartitionMetadata& partition_metadata) {
-    ExpectStoreMetadataMatch(MetadataMatches(partition_metadata));
-  }
-
-  // Expect that target slot is stored with target groups.
-  void ExpectStoreMetadataMatch(
-      const Matcher<MetadataBuilder*>& matcher) override {
-    BootControlAndroidTestP::ExpectStoreMetadataMatch(AllOf(
-        MetadataMatches(PartitionMetadata{
-            .groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
-                       SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB)}}),
-        matcher));
-  }
-};
-
-// Allow to resize within group.
-TEST_P(BootControlAndroidGroupTestP, ResizeWithinGroup) {
-  ExpectStoreMetadata(PartitionMetadata{
-      .groups = {SimpleGroup(T("android"), 3_GiB, T("system"), 3_GiB),
-                 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 2_GiB)}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {SimpleGroup("android", 3_GiB, "system", 3_GiB),
-                     SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
-      true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
-  EXPECT_FALSE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {SimpleGroup("android", 3_GiB, "system", 1_GiB),
-                     SimpleGroup("oem", 2_GiB, "vendor", 3_GiB)}},
-      true))
-      << "Should not be able to grow over maximum size of group";
-}
-
-TEST_P(BootControlAndroidGroupTestP, GroupTooBig) {
-  EXPECT_FALSE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{.groups = {{.name = "android", .size = 3_GiB},
-                                   {.name = "oem", .size = 3_GiB}}},
-      true))
-      << "Should not be able to grow over size of super / 2";
-}
-
-TEST_P(BootControlAndroidGroupTestP, AddPartitionToGroup) {
-  ExpectStoreMetadata(PartitionMetadata{
-      .groups = {{.name = T("android"),
-                  .size = 3_GiB,
-                  .partitions = {{.name = T("system"), .size = 2_GiB},
-                                 {.name = T("system_ext"), .size = 1_GiB}}}}});
-  ExpectUnmap({T("system"), T("vendor"), T("system_ext")});
-
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {{.name = "android",
-                      .size = 3_GiB,
-                      .partitions = {{.name = "system", .size = 2_GiB},
-                                     {.name = "system_ext", .size = 1_GiB}}},
-                     SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
-      true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, RemovePartitionFromGroup) {
-  ExpectStoreMetadata(PartitionMetadata{
-      .groups = {{.name = T("android"), .size = 3_GiB, .partitions = {}}}});
-  ExpectUnmap({T("vendor")});
-
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {{.name = "android", .size = 3_GiB, .partitions = {}},
-                     SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
-      true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, AddGroup) {
-  ExpectStoreMetadata(PartitionMetadata{
-      .groups = {
-          SimpleGroup(T("new_group"), 2_GiB, T("new_partition"), 2_GiB)}});
-  ExpectUnmap({T("system"), T("vendor"), T("new_partition")});
-
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
-                     SimpleGroup("oem", 1_GiB, "vendor", 1_GiB),
-                     SimpleGroup("new_group", 2_GiB, "new_partition", 2_GiB)}},
-      true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, RemoveGroup) {
-  ExpectStoreMetadataMatch(Not(HasGroup(T("oem"))));
-  ExpectUnmap({T("system")});
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB)}},
-      true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, ResizeGroup) {
-  ExpectStoreMetadata(PartitionMetadata{
-      .groups = {SimpleGroup(T("android"), 2_GiB, T("system"), 2_GiB),
-                 SimpleGroup(T("oem"), 3_GiB, T("vendor"), 3_GiB)}});
-  ExpectUnmap({T("system"), T("vendor")});
-
-  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
-      target(),
-      PartitionMetadata{
-          .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
-                     SimpleGroup("oem", 3_GiB, "vendor", 3_GiB)}},
-      true));
-}
-
-INSTANTIATE_TEST_CASE_P(BootControlAndroidTest,
-                        BootControlAndroidGroupTestP,
-                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
-
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 73d7539..dc152cc 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -79,6 +79,8 @@
                              uint32_t target_slot);
 
  private:
+  friend class DynamicPartitionControlAndroidTest;
+
   std::set<std::string> mapped_devices_;
 
   void CleanupInternal(bool wait);
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
new file mode 100644
index 0000000..2fa0f16
--- /dev/null
+++ b/dynamic_partition_control_android_unittest.cc
@@ -0,0 +1,477 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/dynamic_partition_control_android.h"
+
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/dynamic_partition_test_utils.h"
+#include "update_engine/mock_dynamic_partition_control.h"
+
+using std::string;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Not;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroidTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    module_ = std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
+
+    ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+    ON_CALL(dynamicControl(), GetDeviceDir(_))
+        .WillByDefault(Invoke([](auto path) {
+          *path = kFakeDevicePath;
+          return true;
+        }));
+  }
+
+  // Return the mocked DynamicPartitionControlInterface.
+  NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
+    return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(*module_);
+  }
+
+  uint32_t source() { return slots_.source; }
+  uint32_t target() { return slots_.target; }
+
+  // Return partition names with suffix of source().
+  std::string S(const std::string& name) {
+    return name + kSlotSuffixes[source()];
+  }
+
+  // Return partition names with suffix of target().
+  std::string T(const std::string& name) {
+    return name + kSlotSuffixes[target()];
+  }
+
+  // Set the fake metadata to return when LoadMetadataBuilder is called on
+  // |slot|.
+  void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
+    EXPECT_CALL(dynamicControl(),
+                LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+        .Times(AnyNumber())
+        .WillRepeatedly(Invoke([sizes](auto, auto, auto) {
+          return NewFakeMetadata(PartitionSuffixSizesToMetadata(sizes));
+        }));
+  }
+
+  void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
+    EXPECT_CALL(dynamicControl(),
+                StoreMetadata(GetSuperDevice(target()),
+                              MetadataMatches(partition_sizes),
+                              target()))
+        .WillOnce(Return(true));
+  }
+
+  // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
+  // slot with each partition in |partitions|.
+  void ExpectUnmap(const std::set<std::string>& partitions) {
+    // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
+    ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
+        .WillByDefault(Return(false));
+
+    for (const auto& partition : partitions) {
+      EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
+          .WillOnce(Return(true));
+    }
+  }
+  bool PreparePartitionsForUpdate(const PartitionSizes& partition_sizes) {
+    return dynamicControl().PreparePartitionsForUpdate(
+        source(), target(), PartitionSizesToMetadata(partition_sizes));
+  }
+  void SetSlots(const TestParam& slots) { slots_ = slots; }
+
+  struct Listener : public ::testing::MatchResultListener {
+    explicit Listener(std::ostream* os) : MatchResultListener(os) {}
+  };
+
+  testing::AssertionResult UpdatePartitionMetadata(
+      const PartitionSuffixSizes& source_metadata,
+      const PartitionSizes& update_metadata,
+      const PartitionSuffixSizes& expected) {
+    return UpdatePartitionMetadata(
+        PartitionSuffixSizesToMetadata(source_metadata),
+        PartitionSizesToMetadata(update_metadata),
+        PartitionSuffixSizesToMetadata(expected));
+  }
+  testing::AssertionResult UpdatePartitionMetadata(
+      const PartitionMetadata& source_metadata,
+      const PartitionMetadata& update_metadata,
+      const PartitionMetadata& expected) {
+    return UpdatePartitionMetadata(
+        source_metadata, update_metadata, MetadataMatches(expected));
+  }
+  testing::AssertionResult UpdatePartitionMetadata(
+      const PartitionMetadata& source_metadata,
+      const PartitionMetadata& update_metadata,
+      const Matcher<MetadataBuilder*>& matcher) {
+    auto super_metadata = NewFakeMetadata(source_metadata);
+    if (!module_->UpdatePartitionMetadata(
+            super_metadata.get(), target(), update_metadata)) {
+      return testing::AssertionFailure()
+             << "UpdatePartitionMetadataInternal failed";
+    }
+    std::stringstream ss;
+    Listener listener(&ss);
+    if (matcher.MatchAndExplain(super_metadata.get(), &listener)) {
+      return testing::AssertionSuccess() << ss.str();
+    } else {
+      return testing::AssertionFailure() << ss.str();
+    }
+  }
+
+  std::unique_ptr<DynamicPartitionControlAndroid> module_;
+  TestParam slots_;
+};
+
+class DynamicPartitionControlAndroidTestP
+    : public DynamicPartitionControlAndroidTest,
+      public ::testing::WithParamInterface<TestParam> {
+ public:
+  void SetUp() override {
+    DynamicPartitionControlAndroidTest::SetUp();
+    SetSlots(GetParam());
+  }
+};
+
+// Test resize case. Grow if target metadata contains a partition with a size
+// less than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+       NeedGrowIfSizeNotMatchWhenResizing) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB},
+                                {S("vendor"), 1_GiB},
+                                {T("system"), 3_GiB},
+                                {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test resize case. Shrink if target metadata contains a partition with a size
+// greater than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+       NeedShrinkIfSizeNotMatchWhenResizing) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB},
+                                {S("vendor"), 1_GiB},
+                                {T("system"), 2_GiB},
+                                {T("vendor"), 150_MiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 150_MiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test adding partitions on the first run.
+TEST_P(DynamicPartitionControlAndroidTestP, AddPartitionToEmptyMetadata) {
+  PartitionSuffixSizes source_metadata{};
+  PartitionSuffixSizes expected{{T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test subsequent add case.
+TEST_P(DynamicPartitionControlAndroidTestP, AddAdditionalPartition) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {T("system"), 2_GiB}};
+  PartitionSuffixSizes expected{
+      {S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete one partition.
+TEST_P(DynamicPartitionControlAndroidTestP, DeletePartition) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  // No T("vendor")
+  PartitionSuffixSizes expected{
+      {S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete all partitions.
+TEST_P(DynamicPartitionControlAndroidTestP, DeleteAll) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB}, {S("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test corrupt source metadata case.
+TEST_P(DynamicPartitionControlAndroidTestP, CorruptedSourceMetadata) {
+  EXPECT_CALL(dynamicControl(),
+              LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
+      .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
+  ExpectUnmap({T("system")});
+
+  EXPECT_FALSE(PreparePartitionsForUpdate({{"system", 1_GiB}}))
+      << "Should not be able to continue with corrupt source metadata";
+}
+
+// Test that UpdatePartitionMetadata fails if there is not enough space on the
+// device.
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpace) {
+  PartitionSuffixSizes source_metadata{{S("system"), 3_GiB},
+                                       {S("vendor"), 2_GiB},
+                                       {T("system"), 0},
+                                       {T("vendor"), 0}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to fit 11GiB data into 10GiB space";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpaceForSlot) {
+  PartitionSuffixSizes source_metadata{{S("system"), 1_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 0},
+                                       {T("vendor"), 0}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to grow over size of super / 2";
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+                        DynamicPartitionControlAndroidTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+class DynamicPartitionControlAndroidGroupTestP
+    : public DynamicPartitionControlAndroidTestP {
+ public:
+  PartitionMetadata source_metadata;
+  void SetUp() override {
+    DynamicPartitionControlAndroidTestP::SetUp();
+    source_metadata = {
+        .groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
+                   SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB),
+                   SimpleGroup(T("android"), 3_GiB, T("system"), 0),
+                   SimpleGroup(T("oem"), 2_GiB, T("vendor"), 0)}};
+  }
+
+  // Return a simple group with only one partition.
+  PartitionMetadata::Group SimpleGroup(const string& group,
+                                       uint64_t group_size,
+                                       const string& partition,
+                                       uint64_t partition_size) {
+    return {.name = group,
+            .size = group_size,
+            .partitions = {{.name = partition, .size = partition_size}}};
+  }
+};
+
+// Allow to resize within group.
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeWithinGroup) {
+  PartitionMetadata expected{
+      .groups = {SimpleGroup(T("android"), 3_GiB, T("system"), 3_GiB),
+                 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 2_GiB)}};
+
+  PartitionMetadata update_metadata{
+      .groups = {SimpleGroup("android", 3_GiB, "system", 3_GiB),
+                 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
+
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
+  PartitionMetadata update_metadata{
+      .groups = {SimpleGroup("android", 3_GiB, "system", 1_GiB),
+                 SimpleGroup("oem", 2_GiB, "vendor", 3_GiB)}};
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to grow over maximum size of group";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, GroupTooBig) {
+  PartitionMetadata update_metadata{
+      .groups = {{.name = "android", .size = 3_GiB},
+                 {.name = "oem", .size = 3_GiB}}};
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddPartitionToGroup) {
+  PartitionMetadata expected{
+      .groups = {{.name = T("android"),
+                  .size = 3_GiB,
+                  .partitions = {{.name = T("system"), .size = 2_GiB},
+                                 {.name = T("system_ext"), .size = 1_GiB}}}}};
+  PartitionMetadata update_metadata{
+      .groups = {{.name = "android",
+                  .size = 3_GiB,
+                  .partitions = {{.name = "system", .size = 2_GiB},
+                                 {.name = "system_ext", .size = 1_GiB}}},
+                 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemovePartitionFromGroup) {
+  PartitionMetadata expected{
+      .groups = {{.name = T("android"), .size = 3_GiB, .partitions = {}}}};
+  PartitionMetadata update_metadata{
+      .groups = {{.name = "android", .size = 3_GiB, .partitions = {}},
+                 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddGroup) {
+  PartitionMetadata expected{
+      .groups = {
+          SimpleGroup(T("new_group"), 2_GiB, T("new_partition"), 2_GiB)}};
+  PartitionMetadata update_metadata{
+      .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
+                 SimpleGroup("oem", 1_GiB, "vendor", 1_GiB),
+                 SimpleGroup("new_group", 2_GiB, "new_partition", 2_GiB)}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemoveGroup) {
+  PartitionMetadata update_metadata{
+      .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB)}};
+
+  EXPECT_TRUE(UpdatePartitionMetadata(
+      source_metadata, update_metadata, Not(HasGroup(T("oem")))));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeGroup) {
+  PartitionMetadata expected{
+      .groups = {SimpleGroup(T("android"), 2_GiB, T("system"), 2_GiB),
+                 SimpleGroup(T("oem"), 3_GiB, T("vendor"), 3_GiB)}};
+  PartitionMetadata update_metadata{
+      .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
+                 SimpleGroup("oem", 3_GiB, "vendor", 3_GiB)}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+                        DynamicPartitionControlAndroidGroupTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+const PartitionSuffixSizes update_sizes_0() {
+  // Initial state is 0 for "other" slot.
+  return {
+      {"grown_a", 2_GiB},
+      {"shrunk_a", 1_GiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 150_MiB},
+      // no added_a
+      {"grown_b", 200_MiB},
+      // simulate system_other
+      {"shrunk_b", 0},
+      {"same_b", 0},
+      {"deleted_b", 0},
+      // no added_b
+  };
+}
+
+const PartitionSuffixSizes update_sizes_1() {
+  return {
+      {"grown_a", 2_GiB},
+      {"shrunk_a", 1_GiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 150_MiB},
+      // no added_a
+      {"grown_b", 3_GiB},
+      {"shrunk_b", 150_MiB},
+      {"same_b", 100_MiB},
+      {"added_b", 150_MiB},
+      // no deleted_b
+  };
+}
+
+const PartitionSuffixSizes update_sizes_2() {
+  return {
+      {"grown_a", 4_GiB},
+      {"shrunk_a", 100_MiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 64_MiB},
+      // no added_a
+      {"grown_b", 3_GiB},
+      {"shrunk_b", 150_MiB},
+      {"same_b", 100_MiB},
+      {"added_b", 150_MiB},
+      // no deleted_b
+  };
+}
+
+// Test case for first update after the device is manufactured, in which
+// case the "other" slot is likely of size "0" (except system, which is
+// non-zero because of system_other partition)
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedFirstUpdate) {
+  SetSlots({0, 1});
+
+  SetMetadata(source(), update_sizes_0());
+  SetMetadata(target(), update_sizes_0());
+  ExpectStoreMetadata(update_sizes_1());
+  ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
+
+  EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 3_GiB},
+                                          {"shrunk", 150_MiB},
+                                          {"same", 100_MiB},
+                                          {"added", 150_MiB}}));
+}
+
+// After first update, test for the second update. In the second update, the
+// "added" partition is deleted and "deleted" partition is re-added.
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedSecondUpdate) {
+  SetSlots({1, 0});
+
+  SetMetadata(source(), update_sizes_1());
+  SetMetadata(target(), update_sizes_0());
+
+  ExpectStoreMetadata(update_sizes_2());
+  ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+
+  EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 4_GiB},
+                                          {"shrunk", 100_MiB},
+                                          {"same", 100_MiB},
+                                          {"deleted", 64_MiB}}));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dynamic_partition_test_utils.h b/dynamic_partition_test_utils.h
new file mode 100644
index 0000000..2cfdff5
--- /dev/null
+++ b/dynamic_partition_test_utils.h
@@ -0,0 +1,258 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+using android::fs_mgr::MetadataBuilder;
+using testing::_;
+using testing::MakeMatcher;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::MatchResultListener;
+
+constexpr const uint32_t kMaxNumSlots = 2;
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
+constexpr const uint32_t kFakeMetadataSize = 65536;
+constexpr const char* kDefaultGroup = "foo";
+
+// A map describing the size of each partition.
+// "{name, size}"
+using PartitionSizes = std::map<std::string, uint64_t>;
+
+// "{name_a, size}"
+using PartitionSuffixSizes = std::map<std::string, uint64_t>;
+
+using PartitionMetadata = BootControlInterface::PartitionMetadata;
+
+// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
+// of user-defined literal operators.
+// clang-format off
+inline constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
+  return x << 20;
+}
+inline constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
+  return x << 30;
+}
+// clang-format on
+
+constexpr uint64_t kDefaultGroupSize = 5_GiB;
+// Super device size. 1 MiB for metadata.
+constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
+
+template <typename U, typename V>
+inline std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
+  os << "{";
+  bool first = true;
+  for (const auto& pair : param) {
+    if (!first)
+      os << ", ";
+    os << pair.first << ":" << pair.second;
+    first = false;
+  }
+  return os << "}";
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& os, const std::vector<T>& param) {
+  os << "[";
+  bool first = true;
+  for (const auto& e : param) {
+    if (!first)
+      os << ", ";
+    os << e;
+    first = false;
+  }
+  return os << "]";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const PartitionMetadata::Partition& p) {
+  return os << "{" << p.name << ", " << p.size << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const PartitionMetadata::Group& g) {
+  return os << "{" << g.name << ", " << g.size << ", " << g.partitions << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os, const PartitionMetadata& m) {
+  return os << m.groups;
+}
+
+inline std::string GetDevice(const std::string& name) {
+  return kFakeDevicePath + name;
+}
+
+inline std::string GetDmDevice(const std::string& name) {
+  return kFakeDmDevicePath + name;
+}
+
+// TODO(elsk): fs_mgr_get_super_partition_name should be mocked.
+inline std::string GetSuperDevice(uint32_t slot) {
+  return GetDevice(fs_mgr_get_super_partition_name(slot));
+}
+
+// To support legacy tests, auto-convert {name_a: size} map to
+// PartitionMetadata.
+inline PartitionMetadata PartitionSuffixSizesToMetadata(
+    const PartitionSuffixSizes& partition_sizes) {
+  PartitionMetadata metadata;
+  for (const char* suffix : kSlotSuffixes) {
+    metadata.groups.push_back(
+        {std::string(kDefaultGroup) + suffix, kDefaultGroupSize, {}});
+  }
+  for (const auto& pair : partition_sizes) {
+    for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
+      if (base::EndsWith(pair.first,
+                         kSlotSuffixes[suffix_idx],
+                         base::CompareCase::SENSITIVE)) {
+        metadata.groups[suffix_idx].partitions.push_back(
+            {pair.first, pair.second});
+      }
+    }
+  }
+  return metadata;
+}
+
+// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
+inline PartitionMetadata PartitionSizesToMetadata(
+    const PartitionSizes& partition_sizes) {
+  PartitionMetadata metadata;
+  metadata.groups.push_back(
+      {std::string{kDefaultGroup}, kDefaultGroupSize, {}});
+  for (const auto& pair : partition_sizes) {
+    metadata.groups[0].partitions.push_back({pair.first, pair.second});
+  }
+  return metadata;
+}
+
+inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
+    const PartitionMetadata& metadata) {
+  auto builder =
+      MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
+  EXPECT_GE(builder->AllocatableSpace(), kDefaultGroupSize * 2);
+  EXPECT_NE(nullptr, builder);
+  if (builder == nullptr)
+    return nullptr;
+  for (const auto& group : metadata.groups) {
+    EXPECT_TRUE(builder->AddGroup(group.name, group.size));
+    for (const auto& partition : group.partitions) {
+      auto p = builder->AddPartition(partition.name, group.name, 0 /* attr */);
+      EXPECT_TRUE(p && builder->ResizePartition(p, partition.size));
+    }
+  }
+  return builder;
+}
+
+class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
+ public:
+  explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
+      : partition_metadata_(PartitionSuffixSizesToMetadata(partition_sizes)) {}
+  explicit MetadataMatcher(const PartitionMetadata& partition_metadata)
+      : partition_metadata_(partition_metadata) {}
+
+  bool MatchAndExplain(MetadataBuilder* metadata,
+                       MatchResultListener* listener) const override {
+    bool success = true;
+    for (const auto& group : partition_metadata_.groups) {
+      for (const auto& partition : group.partitions) {
+        auto p = metadata->FindPartition(partition.name);
+        if (p == nullptr) {
+          if (!success)
+            *listener << "; ";
+          *listener << "No partition " << partition.name;
+          success = false;
+          continue;
+        }
+        if (p->size() != partition.size) {
+          if (!success)
+            *listener << "; ";
+          *listener << "Partition " << partition.name << " has size "
+                    << p->size() << ", expected " << partition.size;
+          success = false;
+        }
+        if (p->group_name() != group.name) {
+          if (!success)
+            *listener << "; ";
+          *listener << "Partition " << partition.name << " has group "
+                    << p->group_name() << ", expected " << group.name;
+          success = false;
+        }
+      }
+    }
+    return success;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "expect: " << partition_metadata_;
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "expect not: " << partition_metadata_;
+  }
+
+ private:
+  PartitionMetadata partition_metadata_;
+};
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+    const PartitionSuffixSizes& partition_sizes) {
+  return MakeMatcher(new MetadataMatcher(partition_sizes));
+}
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+    const PartitionMetadata& partition_metadata) {
+  return MakeMatcher(new MetadataMatcher(partition_metadata));
+}
+
+MATCHER_P(HasGroup, group, " has group " + group) {
+  auto groups = arg->ListGroups();
+  return std::find(groups.begin(), groups.end(), group) != groups.end();
+}
+
+struct TestParam {
+  uint32_t source;
+  uint32_t target;
+};
+inline std::ostream& operator<<(std::ostream& os, const TestParam& param) {
+  return os << "{source: " << param.source << ", target:" << param.target
+            << "}";
+}
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index a0701e7..72eb030 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -27,6 +27,30 @@
 
 namespace chromeos_update_engine {
 
+class MockDynamicPartitionControl : public DynamicPartitionControlInterface {
+ public:
+  MOCK_METHOD5(MapPartitionOnDeviceMapper,
+               bool(const std::string&,
+                    const std::string&,
+                    uint32_t,
+                    bool,
+                    std::string*));
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD1(DeviceExists, bool(const std::string&));
+  MOCK_METHOD1(GetState, ::android::dm::DmDeviceState(const std::string&));
+  MOCK_METHOD2(GetDmDevicePathByName, bool(const std::string&, std::string*));
+  MOCK_METHOD2(LoadMetadataBuilder,
+               std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
+                   const std::string&, uint32_t));
+  MOCK_METHOD1(GetDeviceDir, bool(std::string*));
+  MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
+  MOCK_METHOD3(PreparePartitionsForUpdate,
+               bool(uint32_t,
+                    uint32_t,
+                    const BootControlInterface::PartitionMetadata&));
+  MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
+};
+
 class MockDynamicPartitionControlAndroid
     : public DynamicPartitionControlAndroid {
  public: