From 7e5bae88b80323d05983418351636bc4f54e18fa Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 14 Aug 2019 19:15:19 -0700 Subject: [PATCH] libsnapshot: Use a real fake super partition for tests. We are currently creating test partitions via ImageManager, which unfortunately leads to a lot of hacks in my local tree to test first-stage init. ImageManager devices look nothing like real partitions in super, and it's a lot of work to massage it to act the same. Instead, let's create an actual super partition. We can do this via ImageManager, and give it its very own partition table. To make this work, we need a special IPartitionOpener that will redirect requests for "super" to our temporary device. A new .cpp file has been added to house this. A few other tidbits have moved there as well. This makes setup code a bit more complicated, but now our tests will behave much closer to a real device. Bug: 139204329 Test: libsnapshot_test gtest Change-Id: I2f40109cd3c1d6343d6f52979789e25f9afd533a --- fs_mgr/libsnapshot/Android.bp | 1 + fs_mgr/libsnapshot/snapshot_test.cpp | 148 ++++++++++++++++++++------- fs_mgr/libsnapshot/test_helpers.cpp | 50 +++++++++ fs_mgr/libsnapshot/test_helpers.h | 58 +++++++++++ 4 files changed, 219 insertions(+), 38 deletions(-) create mode 100644 fs_mgr/libsnapshot/test_helpers.cpp create mode 100644 fs_mgr/libsnapshot/test_helpers.h diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 128a4f927..51f5c50cf 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -74,6 +74,7 @@ cc_test { defaults: ["libsnapshot_defaults"], srcs: [ "snapshot_test.cpp", + "test_helpers.cpp", ], shared_libs: [ "libbinder", diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 438ec2f8f..518c6191a 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -26,9 +26,13 @@ #include #include #include +#include #include #include #include +#include + +#include "test_helpers.h" namespace android { namespace snapshot { @@ -37,25 +41,35 @@ using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using android::fiemap::IImageManager; +using android::fs_mgr::BlockDeviceInfo; +using android::fs_mgr::CreateLogicalPartitionParams; +using android::fs_mgr::MetadataBuilder; using namespace std::chrono_literals; using namespace std::string_literals; -class TestDeviceInfo : public SnapshotManager::IDeviceInfo { - public: - std::string GetGsidDir() const override { return "ota/test"s; } - std::string GetMetadataDir() const override { return "/metadata/ota/test"s; } - std::string GetSlotSuffix() const override { return slot_suffix_; } - - void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; } - - private: - std::string slot_suffix_ = "_a"; -}; - -// These are not reset between each test because it's expensive to reconnect -// to gsid each time. +// These are not reset between each test because it's expensive to create +// these resources (starting+connecting to gsid, zero-filling images). std::unique_ptr sm; TestDeviceInfo* test_device = nullptr; +std::string fake_super; + +static constexpr uint64_t kSuperSize = 16 * 1024 * 1024; + +// Helper to remove stale partitions in fake super. +void CleanupPartitions() { + // These are hardcoded since we might abort in the middle of a test, and + // can't recover the in-use list. + static std::vector kPartitionNames = { + "base-device", + }; + + auto& dm = DeviceMapper::Instance(); + for (const auto& partition : kPartitionNames) { + if (dm.GetState(partition) != DmDeviceState::INVALID) { + dm.DeleteDevice(partition); + } + } +} class SnapshotTest : public ::testing::Test { public: @@ -69,6 +83,7 @@ class SnapshotTest : public ::testing::Test { test_device->set_slot_suffix("_a"); CleanupTestArtifacts(); + FormatFakeSuper(); ASSERT_TRUE(sm->BeginUpdate()); } @@ -96,12 +111,7 @@ class SnapshotTest : public ::testing::Test { android::base::RemoveFileIfExists(status_file); } - // Remove all images. We hardcode the list of names since this can run - // before the test (when cleaning up from a crash). - std::vector temp_partitions = {"base-device"}; - for (const auto& partition : temp_partitions) { - DeleteBackingImage(image_manager_, partition); - } + CleanupPartitions(); if (sm->GetUpdateState() != UpdateState::None) { auto state_file = sm->GetStateFilePath(); @@ -114,11 +124,48 @@ class SnapshotTest : public ::testing::Test { return !!lock_; } - bool CreateTempDevice(const std::string& name, uint64_t size, std::string* path) { - if (!image_manager_->CreateBackingImage(name, size, false)) { + void FormatFakeSuper() { + BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096); + std::vector devices = {super_device}; + + auto builder = MetadataBuilder::New(devices, "super", 65536, 2); + ASSERT_NE(builder, nullptr); + + auto metadata = builder->Export(); + ASSERT_NE(metadata, nullptr); + + TestPartitionOpener opener(fake_super); + ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get())); + } + + // If |path| is non-null, the partition will be mapped after creation. + bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) { + TestPartitionOpener opener(fake_super); + auto builder = MetadataBuilder::New(opener, "super", 0); + if (!builder) return false; + + auto partition = builder->AddPartition(name, 0); + if (!partition) return false; + if (!builder->ResizePartition(partition, size)) { return false; } - return image_manager_->MapImageDevice(name, 10s, path); + + auto metadata = builder->Export(); + if (!metadata) return false; + if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) { + return false; + } + + if (!path) return true; + + CreateLogicalPartitionParams params = { + .block_device = fake_super, + .metadata = metadata.get(), + .partition_name = name, + .force_writable = true, + .timeout_ms = 10s, + }; + return CreateLogicalPartition(params, path); } void DeleteSnapshotDevice(const std::string& snapshot) { @@ -130,18 +177,10 @@ class SnapshotTest : public ::testing::Test { } } - void DeleteBackingImage(IImageManager* manager, const std::string& name) { - if (manager->IsImageMapped(name)) { - ASSERT_TRUE(manager->UnmapImageDevice(name)); - } - if (manager->BackingImageExists(name)) { - ASSERT_TRUE(manager->DeleteBackingImage(name)); - } - } - DeviceMapper& dm_; std::unique_ptr lock_; android::fiemap::IImageManager* image_manager_ = nullptr; + std::string fake_super_; }; TEST_F(SnapshotTest, CreateSnapshot) { @@ -177,7 +216,7 @@ TEST_F(SnapshotTest, MapSnapshot) { kDeviceSize)); std::string base_device; - ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); + ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device)); std::string snap_device; ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); @@ -193,7 +232,7 @@ TEST_F(SnapshotTest, MapPartialSnapshot) { kSnapshotSize)); std::string base_device; - ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); + ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device)); std::string snap_device; ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); @@ -215,7 +254,7 @@ TEST_F(SnapshotTest, Merge) { kDeviceSize)); std::string base_device, snap_device; - ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); + ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); std::string test_string = "This is a test string."; @@ -270,7 +309,7 @@ TEST_F(SnapshotTest, MergeCannotRemoveCow) { kDeviceSize)); std::string base_device, snap_device; - ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device)); + ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device)); ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device)); // Keep an open handle to the cow device. This should cause the merge to @@ -321,10 +360,14 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); std::vector paths = { + // clang-format off "/data/gsi/ota/test", + "/data/gsi/ota/test/super", "/metadata/gsi/ota/test", + "/metadata/gsi/ota/test/super", "/metadata/ota/test", "/metadata/ota/test/snapshots", + // clang-format on }; for (const auto& path : paths) { if (!Mkdir(path)) { @@ -336,9 +379,38 @@ int main(int argc, char** argv) { test_device = new TestDeviceInfo(); sm = SnapshotManager::New(test_device); if (!sm) { - std::cerr << "Could not create snapshot manager"; + std::cerr << "Could not create snapshot manager\n"; return 1; } - return RUN_ALL_TESTS(); + // Use a separate image manager for our fake super partition. + auto super_images = IImageManager::Open("ota/test/super", 10s); + if (!super_images) { + std::cerr << "Could not create image manager\n"; + return 1; + } + + // Clean up previous run. + CleanupPartitions(); + DeleteBackingImage(super_images.get(), "fake-super"); + + // Create and map the fake super partition. + static constexpr int kImageFlags = + IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL; + if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) { + std::cerr << "Could not create fake super partition\n"; + return 1; + } + if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) { + std::cerr << "Could not map fake super partition\n"; + return 1; + } + + auto result = RUN_ALL_TESTS(); + + // Clean up again. + CleanupPartitions(); + DeleteBackingImage(super_images.get(), "fake-super"); + + return result; } diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp new file mode 100644 index 000000000..17ffa4ea0 --- /dev/null +++ b/fs_mgr/libsnapshot/test_helpers.cpp @@ -0,0 +1,50 @@ +// 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 "test_helpers.h" + +#include + +namespace android { +namespace snapshot { + +using android::fiemap::IImageManager; + +void DeleteBackingImage(IImageManager* manager, const std::string& name) { + if (manager->IsImageMapped(name)) { + ASSERT_TRUE(manager->UnmapImageDevice(name)); + } + if (manager->BackingImageExists(name)) { + ASSERT_TRUE(manager->DeleteBackingImage(name)); + } +} + +android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name, + int flags) const { + if (partition_name == "super") { + return PartitionOpener::Open(fake_super_path_, flags); + } + return PartitionOpener::Open(partition_name, flags); +} + +bool TestPartitionOpener::GetInfo(const std::string& partition_name, + android::fs_mgr::BlockDeviceInfo* info) const { + if (partition_name == "super") { + return PartitionOpener::GetInfo(fake_super_path_, info); + } + return PartitionOpener::GetInfo(partition_name, info); +} + +} // namespace snapshot +} // namespace android diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h new file mode 100644 index 000000000..9491be31c --- /dev/null +++ b/fs_mgr/libsnapshot/test_helpers.h @@ -0,0 +1,58 @@ +// 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. + +#pragma once + +#include + +#include +#include +#include + +namespace android { +namespace snapshot { + +using namespace std::string_literals; + +class TestDeviceInfo : public SnapshotManager::IDeviceInfo { + public: + std::string GetGsidDir() const override { return "ota/test"s; } + std::string GetMetadataDir() const override { return "/metadata/ota/test"s; } + std::string GetSlotSuffix() const override { return slot_suffix_; } + + void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; } + + private: + std::string slot_suffix_ = "_a"; +}; + +// Redirect requests for "super" to our fake super partition. +class TestPartitionOpener final : public android::fs_mgr::PartitionOpener { + public: + explicit TestPartitionOpener(const std::string& fake_super_path) + : fake_super_path_(fake_super_path) {} + + android::base::unique_fd Open(const std::string& partition_name, int flags) const override; + bool GetInfo(const std::string& partition_name, + android::fs_mgr::BlockDeviceInfo* info) const override; + + private: + std::string fake_super_path_; +}; + +// Helper for error-spam-free cleanup. +void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name); + +} // namespace snapshot +} // namespace android