|  | // | 
|  | // Copyright (C) 2018 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/boot_control_android.h" | 
|  |  | 
|  | #include <set> | 
|  |  | 
|  | #include <android-base/strings.h> | 
|  | #include <gmock/gmock.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "update_engine/mock_boot_control_hal.h" | 
|  | #include "update_engine/mock_dynamic_partition_control.h" | 
|  |  | 
|  | using android::base::Join; | 
|  | using android::fs_mgr::MetadataBuilder; | 
|  | using android::hardware::Void; | 
|  | 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::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* kFakeMappedPath = "/fake/mapped/path/"; | 
|  | constexpr const uint32_t kFakeMetadataSize = 65536; | 
|  |  | 
|  | // A map describing the size of each partition. | 
|  | using PartitionSizes = std::map<std::string, uint64_t>; | 
|  |  | 
|  | // C++ standards do not allow uint64_t (aka unsigned long) to be the parameter | 
|  | // of user-defined literal operators. | 
|  | unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT | 
|  | return x << 20; | 
|  | } | 
|  | unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT | 
|  | return x << 30; | 
|  | } | 
|  |  | 
|  | 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 << "}"; | 
|  | } | 
|  |  | 
|  | inline std::string GetDevice(const std::string& name) { | 
|  | return kFakeDevicePath + name; | 
|  | } | 
|  | inline std::string GetSuperDevice() { | 
|  | return GetDevice(LP_METADATA_PARTITION_NAME); | 
|  | } | 
|  |  | 
|  | 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 | 
|  | << "}"; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<MetadataBuilder> NewFakeMetadata(const PartitionSizes& sizes) { | 
|  | auto builder = MetadataBuilder::New(10_GiB, kFakeMetadataSize, kMaxNumSlots); | 
|  | EXPECT_NE(nullptr, builder); | 
|  | if (builder == nullptr) | 
|  | return nullptr; | 
|  | for (const auto& pair : sizes) { | 
|  | auto p = builder->AddPartition(pair.first, 0 /* attr */); | 
|  | EXPECT_TRUE(p && builder->ResizePartition(p, pair.second)); | 
|  | } | 
|  | return builder; | 
|  | } | 
|  |  | 
|  | class MetadataMatcher : public MatcherInterface<MetadataBuilder*> { | 
|  | public: | 
|  | explicit MetadataMatcher(const PartitionSizes& partition_sizes) | 
|  | : partition_sizes_(partition_sizes) {} | 
|  | bool MatchAndExplain(MetadataBuilder* metadata, | 
|  | MatchResultListener* listener) const override { | 
|  | bool success = true; | 
|  | for (const auto& pair : partition_sizes_) { | 
|  | auto p = metadata->FindPartition(pair.first); | 
|  | if (p == nullptr) { | 
|  | if (success) | 
|  | *listener << "; "; | 
|  | *listener << "No partition " << pair.first; | 
|  | success = false; | 
|  | continue; | 
|  | } | 
|  | if (p->size() != pair.second) { | 
|  | if (success) | 
|  | *listener << "; "; | 
|  | *listener << "Partition " << pair.first << " has size " << p->size() | 
|  | << ", expected " << pair.second; | 
|  | success = false; | 
|  | } | 
|  | } | 
|  | return success; | 
|  | } | 
|  |  | 
|  | void DescribeTo(std::ostream* os) const override { | 
|  | *os << "expect: " << partition_sizes_; | 
|  | } | 
|  |  | 
|  | void DescribeNegationTo(std::ostream* os) const override { | 
|  | *os << "expect not: " << partition_sizes_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | PartitionSizes partition_sizes_; | 
|  | }; | 
|  |  | 
|  | inline Matcher<MetadataBuilder*> MetadataMatches( | 
|  | const PartitionSizes& partition_sizes) { | 
|  | return MakeMatcher(new MetadataMatcher(partition_sizes)); | 
|  | } | 
|  |  | 
|  | class BootControlAndroidTest : public ::testing::Test { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | // Fake init bootctl_ | 
|  | bootctl_.module_ = new NiceMock<MockBootControlHal>(); | 
|  | bootctl_.dynamic_control_ = | 
|  | std::make_unique<NiceMock<MockDynamicPartitionControl>>(); | 
|  |  | 
|  | ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] { | 
|  | return kMaxNumSlots; | 
|  | })); | 
|  | ON_CALL(module(), getSuffix(_, _)) | 
|  | .WillByDefault(Invoke([](auto slot, auto cb) { | 
|  | EXPECT_LE(slot, kMaxNumSlots); | 
|  | cb(slot < kMaxNumSlots ? kSlotSuffixes[slot] : ""); | 
|  | return Void(); | 
|  | })); | 
|  |  | 
|  | ON_CALL(dynamicControl(), IsDynamicPartitionsEnabled()) | 
|  | .WillByDefault(Return(true)); | 
|  | ON_CALL(dynamicControl(), GetDeviceDir(_)) | 
|  | .WillByDefault(Invoke([](auto path) { | 
|  | *path = kFakeDevicePath; | 
|  | return true; | 
|  | })); | 
|  | } | 
|  |  | 
|  | // Return the mocked HAL module. | 
|  | NiceMock<MockBootControlHal>& module() { | 
|  | return static_cast<NiceMock<MockBootControlHal>&>(*bootctl_.module_); | 
|  | } | 
|  |  | 
|  | // Return the mocked DynamicPartitionControlInterface. | 
|  | 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 PartitionSizes& sizes) { | 
|  | EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), slot)) | 
|  | .WillOnce( | 
|  | Invoke([sizes](auto, auto) { return NewFakeMetadata(sizes); })); | 
|  | } | 
|  |  | 
|  | // Expect that MapPartitionOnDeviceMapper is called on target() metadata slot | 
|  | // with each partition in |partitions|. | 
|  | void ExpectMap(const std::set<std::string>& partitions) { | 
|  | // Error when MapPartitionOnDeviceMapper is called on unknown arguments. | 
|  | ON_CALL(dynamicControl(), MapPartitionOnDeviceMapper(_, _, _, _)) | 
|  | .WillByDefault(Return(false)); | 
|  |  | 
|  | for (const auto& partition : partitions) { | 
|  | EXPECT_CALL( | 
|  | dynamicControl(), | 
|  | MapPartitionOnDeviceMapper(GetSuperDevice(), partition, target(), _)) | 
|  | .WillOnce(Invoke([this](auto, auto partition, auto, auto path) { | 
|  | auto it = mapped_devices_.find(partition); | 
|  | if (it != mapped_devices_.end()) { | 
|  | *path = it->second; | 
|  | return true; | 
|  | } | 
|  | mapped_devices_[partition] = *path = kFakeMappedPath + partition; | 
|  | 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(Invoke([this](auto partition, auto) { | 
|  | mapped_devices_.erase(partition); | 
|  | return true; | 
|  | })); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ExpectRemap(const std::set<std::string>& partitions) { | 
|  | ExpectUnmap(partitions); | 
|  | ExpectMap(partitions); | 
|  | } | 
|  |  | 
|  | void ExpectDevicesAreMapped(const std::set<std::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."; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 + std::string(kSlotSuffixes[source()]); | 
|  | } | 
|  |  | 
|  | // Return partition names with suffix of target(). | 
|  | std::string T(const std::string& name) { | 
|  | return name + std::string(kSlotSuffixes[target()]); | 
|  | } | 
|  |  | 
|  | // Set source and target slots to use before testing. | 
|  | void SetSlots(const TestParam& slots) { | 
|  | slots_ = slots; | 
|  |  | 
|  | ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] { | 
|  | return source(); | 
|  | })); | 
|  | // Should not store metadata to source slot. | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, source())) | 
|  | .Times(0); | 
|  | } | 
|  |  | 
|  | BootControlAndroid bootctl_;  // BootControlAndroid under test. | 
|  | TestParam slots_; | 
|  | // mapped devices through MapPartitionOnDeviceMapper. | 
|  | std::map<std::string, std::string> mapped_devices_; | 
|  | }; | 
|  |  | 
|  | class BootControlAndroidTestP | 
|  | : public BootControlAndroidTest, | 
|  | public ::testing::WithParamInterface<TestParam> { | 
|  | public: | 
|  | void SetUp() override { | 
|  | BootControlAndroidTest::SetUp(); | 
|  | SetSlots(GetParam()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Test no resize if no dynamic partitions at all. | 
|  | TEST_P(BootControlAndroidTestP, NoResizeIfNoDynamicPartitions) { | 
|  | SetMetadata(source(), {}); | 
|  | SetMetadata(target(), {}); | 
|  | // Should not need to resize and store metadata | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target())) | 
|  | .Times(0); | 
|  | EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_a")))) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Return(true)); | 
|  | EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_b")))) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), {{"static", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({}); | 
|  | } | 
|  |  | 
|  | // Test no resize if update manifest does not contain any dynamic partitions | 
|  | TEST_P(BootControlAndroidTestP, NoResizeIfEmptyMetadata) { | 
|  | SetMetadata(source(), | 
|  | {{S("system"), 4_GiB}, | 
|  | {S("vendor"), 100_MiB}, | 
|  | {T("system"), 3_GiB}, | 
|  | {T("vendor"), 150_MiB}}); | 
|  | SetMetadata(target(), | 
|  | {{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 3_GiB}, | 
|  | {T("vendor"), 150_MiB}}); | 
|  | // Should not need to resize and store metadata | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target())) | 
|  | .Times(0); | 
|  | EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_a")))) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Return(true)); | 
|  | EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_b")))) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), {{"static", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({}); | 
|  | } | 
|  |  | 
|  | // Do not resize if manifest size matches size in target metadata. When resuming | 
|  | // from an update, do not redo the resize if not needed. | 
|  | TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenResizing) { | 
|  | SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}}); | 
|  | SetMetadata(target(), | 
|  | {{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 3_GiB}, | 
|  | {T("vendor"), 1_GiB}}); | 
|  | // Should not need to resize and store metadata | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target())) | 
|  | .Times(0); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 3_GiB}, {"vendor", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Do not resize if manifest size matches size in target metadata. When resuming | 
|  | // from an update, do not redo the resize if not needed. | 
|  | TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenAdding) { | 
|  | SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}}); | 
|  | SetMetadata( | 
|  | target(), | 
|  | {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}}); | 
|  | // Should not need to resize and store metadata | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target())) | 
|  | .Times(0); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Do not resize if manifest size matches size in target metadata. When resuming | 
|  | // from an update, do not redo the resize if not needed. | 
|  | TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenDeleting) { | 
|  | SetMetadata(source(), | 
|  | {{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}); | 
|  | SetMetadata(target(), | 
|  | {{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 0}}); | 
|  | // Should not need to resize and store metadata | 
|  | EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target())) | 
|  | .Times(0); | 
|  | ExpectUnmap({T("system"), T("vendor")}); | 
|  | ExpectMap({T("system")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 0}})); | 
|  | ExpectDevicesAreMapped({T("system")}); | 
|  | } | 
|  |  | 
|  | // Test resize case. Grow if target metadata contains a partition with a size | 
|  | // less than expected. | 
|  | TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) { | 
|  | PartitionSizes initial{{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}; | 
|  | SetMetadata(source(), initial); | 
|  | SetMetadata(target(), initial); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 3_GiB}, | 
|  | {T("vendor"), 1_GiB}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 3_GiB}, {"vendor", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Test resize case. Shrink if target metadata contains a partition with a size | 
|  | // greater than expected. | 
|  | TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) { | 
|  | PartitionSizes initial{{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}; | 
|  | SetMetadata(source(), initial); | 
|  | SetMetadata(target(), initial); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 150_MiB}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 150_MiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Test adding partitions on the first run. | 
|  | TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) { | 
|  | SetMetadata(source(), {}); | 
|  | SetMetadata(target(), {}); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata( | 
|  | GetSuperDevice(), | 
|  | MetadataMatches({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Test subsequent add case. | 
|  | TEST_P(BootControlAndroidTestP, AddAdditionalPartition) { | 
|  | SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}}); | 
|  | SetMetadata(target(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}}); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 1_GiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Test delete one partition. | 
|  | TEST_P(BootControlAndroidTestP, DeletePartition) { | 
|  | PartitionSizes initial{{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}; | 
|  | SetMetadata(source(), initial); | 
|  | SetMetadata(target(), initial); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 0}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectUnmap({T("system"), T("vendor")}); | 
|  | ExpectMap({T("system")}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 2_GiB}, {"vendor", 0}})); | 
|  | ExpectDevicesAreMapped({T("system")}); | 
|  | } | 
|  |  | 
|  | // Test delete all partitions. | 
|  | TEST_P(BootControlAndroidTestP, DeleteAll) { | 
|  | PartitionSizes initial{{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 2_GiB}, | 
|  | {T("vendor"), 1_GiB}}; | 
|  | SetMetadata(source(), initial); | 
|  | SetMetadata(target(), initial); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 0}, | 
|  | {T("vendor"), 0}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectUnmap({T("system"), T("vendor")}); | 
|  | ExpectMap({}); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | bootctl_.InitPartitionMetadata(target(), {{"system", 0}, {"vendor", 0}})); | 
|  | ExpectDevicesAreMapped({}); | 
|  | } | 
|  |  | 
|  | // Test corrupt source metadata case. This shouldn't happen in practice, | 
|  | // because the device is already booted normally. | 
|  | TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) { | 
|  | EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), source())) | 
|  | .WillOnce(Invoke([](auto, auto) { return nullptr; })); | 
|  | EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {})) | 
|  | << "Should not be able to continue with corrupt source metadata"; | 
|  | } | 
|  |  | 
|  | // Test corrupt target metadata case. This may happen in practice. | 
|  | // BootControlAndroid should copy from source metadata and make necessary | 
|  | // modifications on it. | 
|  | TEST_P(BootControlAndroidTestP, CorruptedTargetMetadata) { | 
|  | SetMetadata(source(), | 
|  | {{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 0}, | 
|  | {T("vendor"), 0}}); | 
|  | EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), target())) | 
|  | .WillOnce(Invoke([](auto, auto) { return nullptr; })); | 
|  | EXPECT_CALL(dynamicControl(), | 
|  | StoreMetadata(GetSuperDevice(), | 
|  | MetadataMatches({{S("system"), 2_GiB}, | 
|  | {S("vendor"), 1_GiB}, | 
|  | {T("system"), 3_GiB}, | 
|  | {T("vendor"), 150_MiB}}), | 
|  | target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectRemap({T("system"), T("vendor")}); | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 3_GiB}, {"vendor", 150_MiB}})); | 
|  | ExpectDevicesAreMapped({T("system"), T("vendor")}); | 
|  | } | 
|  |  | 
|  | // Test that InitPartitionMetadata fail if there is not enough space on the | 
|  | // device. | 
|  | TEST_P(BootControlAndroidTestP, NotEnoughSpace) { | 
|  | PartitionSizes initial{{S("system"), 3_GiB}, | 
|  | {S("vendor"), 2_GiB}, | 
|  | {T("system"), 0}, | 
|  | {T("vendor"), 0}}; | 
|  | SetMetadata(source(), initial); | 
|  | SetMetadata(target(), initial); | 
|  | EXPECT_FALSE(bootctl_.InitPartitionMetadata( | 
|  | target(), {{"system", 3_GiB}, {"vendor", 3_GiB}})) | 
|  | << "Should not be able to fit 11GiB data into 10GiB space"; | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P(ParamTest, | 
|  | BootControlAndroidTestP, | 
|  | testing::Values(TestParam{0, 1}, TestParam{1, 0})); | 
|  |  | 
|  | const PartitionSizes update_sizes_0() { | 
|  | return {{"grown_a", 2_GiB}, | 
|  | {"shrunk_a", 1_GiB}, | 
|  | {"same_a", 100_MiB}, | 
|  | {"deleted_a", 150_MiB}, | 
|  | {"grown_b", 200_MiB}, | 
|  | {"shrunk_b", 0}, | 
|  | {"same_b", 0}}; | 
|  | } | 
|  |  | 
|  | const PartitionSizes update_sizes_1() { | 
|  | return { | 
|  | {"grown_a", 2_GiB}, | 
|  | {"shrunk_a", 1_GiB}, | 
|  | {"same_a", 100_MiB}, | 
|  | {"deleted_a", 150_MiB}, | 
|  | {"grown_b", 3_GiB}, | 
|  | {"shrunk_b", 150_MiB}, | 
|  | {"same_b", 100_MiB}, | 
|  | {"added_b", 150_MiB}, | 
|  | {"deleted_b", 0}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | const PartitionSizes update_sizes_2() { | 
|  | return {{"grown_a", 4_GiB}, | 
|  | {"shrunk_a", 100_MiB}, | 
|  | {"same_a", 100_MiB}, | 
|  | {"added_a", 0_MiB}, | 
|  | {"deleted_a", 64_MiB}, | 
|  | {"grown_b", 3_GiB}, | 
|  | {"shrunk_b", 150_MiB}, | 
|  | {"same_b", 100_MiB}, | 
|  | {"added_b", 150_MiB}, | 
|  | {"deleted_b", 0}}; | 
|  | } | 
|  |  | 
|  | // 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()); | 
|  | EXPECT_CALL( | 
|  | dynamicControl(), | 
|  | StoreMetadata( | 
|  | GetSuperDevice(), MetadataMatches(update_sizes_1()), target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b", "deleted_b"}); | 
|  | ExpectMap({"grown_b", "shrunk_b", "same_b", "added_b"}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), | 
|  | {{"grown", 3_GiB}, | 
|  | {"shrunk", 150_MiB}, | 
|  | {"same", 100_MiB}, | 
|  | {"added", 150_MiB}, | 
|  | {"deleted", 0_MiB}})); | 
|  | ExpectDevicesAreMapped({"grown_b", "shrunk_b", "same_b", "added_b"}); | 
|  | } | 
|  |  | 
|  | // 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()); | 
|  |  | 
|  | EXPECT_CALL( | 
|  | dynamicControl(), | 
|  | StoreMetadata( | 
|  | GetSuperDevice(), MetadataMatches(update_sizes_2()), target())) | 
|  | .WillOnce(Return(true)); | 
|  | ExpectUnmap({"grown_a", "shrunk_a", "same_a", "added_a", "deleted_a"}); | 
|  | ExpectMap({"grown_a", "shrunk_a", "same_a", "deleted_a"}); | 
|  |  | 
|  | EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), | 
|  | {{"grown", 4_GiB}, | 
|  | {"shrunk", 100_MiB}, | 
|  | {"same", 100_MiB}, | 
|  | {"added", 0_MiB}, | 
|  | {"deleted", 64_MiB}})); | 
|  | ExpectDevicesAreMapped({"grown_a", "shrunk_a", "same_a", "deleted_a"}); | 
|  | } | 
|  |  | 
|  | TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) { | 
|  | SetSlots({1, 1}); | 
|  | EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {})) | 
|  | << "Should not be able to apply to current slot."; | 
|  | } | 
|  |  | 
|  | }  // namespace chromeos_update_engine |