// // 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 "snapshot_metadata_updater.h" #include #include #include #include #include #include #include #include using namespace android::storage_literals; using android::fs_mgr::LpMetadata; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::SlotSuffixForSlotNumber; using chromeos_update_engine::DeltaArchiveManifest; using chromeos_update_engine::DynamicPartitionGroup; using chromeos_update_engine::PartitionUpdate; using testing::AssertionFailure; using testing::AssertionResult; using testing::AssertionSuccess; namespace android { namespace snapshot { class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam { public: SnapshotMetadataUpdaterTest() = default; void SetUp() override { SKIP_IF_NON_VIRTUAL_AB(); target_slot_ = GetParam(); target_suffix_ = SlotSuffixForSlotNumber(target_slot_); SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_)); builder_ = MetadataBuilder::New(4_GiB + 1_MiB, 4_KiB, 2); group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups(); group_->set_name("group"); group_->set_size(4_GiB); group_->add_partition_names("system"); group_->add_partition_names("vendor"); system_ = manifest_.add_partitions(); system_->set_partition_name("system"); SetSize(system_, 2_GiB); vendor_ = manifest_.add_partitions(); vendor_->set_partition_name("vendor"); SetSize(vendor_, 1_GiB); ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_)); } void TearDown() override { RETURN_IF_NON_VIRTUAL_AB(); SnapshotTestPropertyFetcher::TearDown(); } // Append suffix to name. std::string T(std::string_view name) { return std::string(name) + target_suffix_; } AssertionResult UpdateAndExport() { SnapshotMetadataUpdater updater(builder_.get(), target_slot_, manifest_); if (!updater.Update()) { return AssertionFailure() << "Update failed."; } exported_ = builder_->Export(); if (exported_ == nullptr) { return AssertionFailure() << "Export failed."; } return AssertionSuccess(); } // Check that in |builder_|, partition |name| + |target_suffix_| has the given |size|. AssertionResult CheckSize(std::string_view name, uint64_t size) { auto p = builder_->FindPartition(T(name)); if (p == nullptr) { return AssertionFailure() << "Cannot find partition " << T(name); } if (p->size() != size) { return AssertionFailure() << "Partition " << T(name) << " should be " << size << " bytes, but is " << p->size() << " bytes."; } return AssertionSuccess() << "Partition" << T(name) << " is " << size << " bytes."; } // Check that in |builder_|, group |name| + |target_suffix_| has the given |size|. AssertionResult CheckGroupSize(std::string_view name, uint64_t size) { auto g = builder_->FindGroup(T(name)); if (g == nullptr) { return AssertionFailure() << "Cannot find group " << T(name); } if (g->maximum_size() != size) { return AssertionFailure() << "Group " << T(name) << " should be " << size << " bytes, but is " << g->maximum_size() << " bytes."; } return AssertionSuccess() << "Group" << T(name) << " is " << size << " bytes."; } // Check that in |builder_|, partition |partition_name| + |target_suffix_| is in group // |group_name| + |target_suffix_|; AssertionResult CheckGroupName(std::string_view partition_name, std::string_view group_name) { auto p = builder_->FindPartition(T(partition_name)); if (p == nullptr) { return AssertionFailure() << "Cannot find partition " << T(partition_name); } if (p->group_name() != T(group_name)) { return AssertionFailure() << "Partition " << T(partition_name) << " should be in " << T(group_name) << ", but is in " << p->group_name() << "."; } return AssertionSuccess() << "Partition" << T(partition_name) << " is in " << T(group_name) << "."; } std::unique_ptr builder_; uint32_t target_slot_; std::string target_suffix_; DeltaArchiveManifest manifest_; std::unique_ptr exported_; DynamicPartitionGroup* group_ = nullptr; PartitionUpdate* system_ = nullptr; PartitionUpdate* vendor_ = nullptr; }; TEST_P(SnapshotMetadataUpdaterTest, NoChange) { EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckGroupSize("group", 4_GiB)); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_TRUE(CheckGroupName("system", "group")); EXPECT_TRUE(CheckSize("vendor", 1_GiB)); EXPECT_TRUE(CheckGroupName("vendor", "group")); } TEST_P(SnapshotMetadataUpdaterTest, GrowWithinBounds) { SetSize(system_, 2_GiB + 512_MiB); SetSize(vendor_, 1_GiB + 512_MiB); ASSERT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB + 512_MiB)); EXPECT_TRUE(CheckSize("vendor", 1_GiB + 512_MiB)); } TEST_P(SnapshotMetadataUpdaterTest, GrowOverSuper) { SetSize(system_, 3_GiB); SetSize(vendor_, 1_GiB + 512_MiB); EXPECT_FALSE(UpdateAndExport()); } TEST_P(SnapshotMetadataUpdaterTest, GrowOverGroup) { SetSize(system_, 3_GiB); SetSize(vendor_, 1_GiB + 4_KiB); EXPECT_FALSE(UpdateAndExport()); } TEST_P(SnapshotMetadataUpdaterTest, Add) { group_->add_partition_names("product"); auto product = manifest_.add_partitions(); product->set_partition_name("product"); SetSize(product, 1_GiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_TRUE(CheckSize("vendor", 1_GiB)); EXPECT_TRUE(CheckSize("product", 1_GiB)); } TEST_P(SnapshotMetadataUpdaterTest, AddTooBig) { group_->add_partition_names("product"); auto product = manifest_.add_partitions(); product->set_partition_name("product"); SetSize(product, 1_GiB + 4_KiB); EXPECT_FALSE(UpdateAndExport()); } TEST_P(SnapshotMetadataUpdaterTest, ShrinkAll) { SetSize(system_, 1_GiB); SetSize(vendor_, 512_MiB); ASSERT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 1_GiB)); EXPECT_TRUE(CheckSize("vendor", 512_MiB)); } TEST_P(SnapshotMetadataUpdaterTest, ShrinkAndGrow) { SetSize(system_, 3_GiB + 512_MiB); SetSize(vendor_, 512_MiB); ASSERT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 3_GiB + 512_MiB)); EXPECT_TRUE(CheckSize("vendor", 512_MiB)); } TEST_P(SnapshotMetadataUpdaterTest, ShrinkAndAdd) { SetSize(system_, 2_GiB); SetSize(vendor_, 512_MiB); group_->add_partition_names("product"); auto product = manifest_.add_partitions(); product->set_partition_name("product"); SetSize(product, 1_GiB + 512_MiB); ASSERT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_TRUE(CheckSize("vendor", 512_MiB)); EXPECT_TRUE(CheckSize("product", 1_GiB + 512_MiB)); } TEST_P(SnapshotMetadataUpdaterTest, Delete) { group_->mutable_partition_names()->RemoveLast(); // No need to delete it from manifest.partitions as SnapshotMetadataUpdater // should ignore them (treat them as static partitions). EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_EQ(nullptr, builder_->FindPartition(T("vendor"))); } TEST_P(SnapshotMetadataUpdaterTest, DeleteAndGrow) { group_->mutable_partition_names()->RemoveLast(); SetSize(system_, 4_GiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 4_GiB)); } TEST_P(SnapshotMetadataUpdaterTest, DeleteAndAdd) { group_->mutable_partition_names()->RemoveLast(); group_->add_partition_names("product"); auto product = manifest_.add_partitions(); product->set_partition_name("product"); SetSize(product, 2_GiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_EQ(nullptr, builder_->FindPartition(T("vendor"))); EXPECT_TRUE(CheckSize("product", 2_GiB)); } TEST_P(SnapshotMetadataUpdaterTest, GrowGroup) { group_->set_size(4_GiB + 512_KiB); SetSize(system_, 2_GiB + 256_KiB); SetSize(vendor_, 2_GiB + 256_KiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 2_GiB + 256_KiB)); EXPECT_TRUE(CheckSize("vendor", 2_GiB + 256_KiB)); } TEST_P(SnapshotMetadataUpdaterTest, ShrinkGroup) { group_->set_size(1_GiB); SetSize(system_, 512_MiB); SetSize(vendor_, 512_MiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckSize("system", 512_MiB)); EXPECT_TRUE(CheckSize("vendor", 512_MiB)); } TEST_P(SnapshotMetadataUpdaterTest, MoveToNewGroup) { group_->mutable_partition_names()->RemoveLast(); group_->set_size(2_GiB); auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups(); another_group->set_name("another_group"); another_group->set_size(2_GiB); another_group->add_partition_names("vendor"); SetSize(vendor_, 2_GiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_TRUE(CheckGroupSize("group", 2_GiB)); EXPECT_TRUE(CheckGroupSize("another_group", 2_GiB)); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_TRUE(CheckGroupName("system", "group")); EXPECT_TRUE(CheckSize("vendor", 2_GiB)); EXPECT_TRUE(CheckGroupName("vendor", "another_group")); } TEST_P(SnapshotMetadataUpdaterTest, DeleteAndAddGroup) { manifest_.mutable_dynamic_partition_metadata()->mutable_groups()->RemoveLast(); group_ = nullptr; auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups(); another_group->set_name("another_group"); another_group->set_size(4_GiB); another_group->add_partition_names("system"); another_group->add_partition_names("vendor"); another_group->add_partition_names("product"); auto product = manifest_.add_partitions(); product->set_partition_name("product"); SetSize(product, 1_GiB); EXPECT_TRUE(UpdateAndExport()); EXPECT_EQ(nullptr, builder_->FindGroup(T("group"))); EXPECT_TRUE(CheckGroupSize("another_group", 4_GiB)); EXPECT_TRUE(CheckSize("system", 2_GiB)); EXPECT_TRUE(CheckGroupName("system", "another_group")); EXPECT_TRUE(CheckSize("vendor", 1_GiB)); EXPECT_TRUE(CheckGroupName("vendor", "another_group")); EXPECT_TRUE(CheckSize("product", 1_GiB)); EXPECT_TRUE(CheckGroupName("product", "another_group")); } INSTANTIATE_TEST_SUITE_P(Snapshot, SnapshotMetadataUpdaterTest, testing::Values(0, 1)); } // namespace snapshot } // namespace android