| // 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 <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_set> |
| |
| #include <android-base/file.h> |
| #include <android/hardware/boot/1.1/IBootControl.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <libfiemap/image_manager.h> |
| #include <liblp/mock_property_fetcher.h> |
| #include <liblp/partition_opener.h> |
| #include <libsnapshot/snapshot.h> |
| #include <storage_literals/storage_literals.h> |
| #include <update_engine/update_metadata.pb.h> |
| |
| namespace android { |
| namespace snapshot { |
| |
| using android::fs_mgr::IPropertyFetcher; |
| using android::fs_mgr::MetadataBuilder; |
| using android::fs_mgr::testing::MockPropertyFetcher; |
| using android::hardware::boot::V1_1::MergeStatus; |
| using chromeos_update_engine::DeltaArchiveManifest; |
| using chromeos_update_engine::PartitionUpdate; |
| using testing::_; |
| using testing::AssertionResult; |
| using testing::NiceMock; |
| |
| using namespace android::storage_literals; |
| using namespace std::string_literals; |
| |
| // These are not reset between each test because it's expensive to create |
| // these resources (starting+connecting to gsid, zero-filling images). |
| extern std::unique_ptr<SnapshotManager> sm; |
| extern class TestDeviceInfo* test_device; |
| extern std::string fake_super; |
| static constexpr uint64_t kSuperSize = 32_MiB + 4_KiB; |
| static constexpr uint64_t kGroupSize = 32_MiB; |
| |
| // 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; |
| std::string GetDeviceString(const std::string& partition_name) const override; |
| |
| private: |
| std::string fake_super_path_; |
| }; |
| |
| class TestDeviceInfo : public SnapshotManager::IDeviceInfo { |
| public: |
| TestDeviceInfo() {} |
| explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); } |
| TestDeviceInfo(const std::string& fake_super, const std::string& slot_suffix) |
| : TestDeviceInfo(fake_super) { |
| set_slot_suffix(slot_suffix); |
| } |
| std::string GetMetadataDir() const override { return "/metadata/ota/test"s; } |
| std::string GetSlotSuffix() const override { return slot_suffix_; } |
| std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; } |
| std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; } |
| const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override { |
| return *opener_.get(); |
| } |
| bool SetBootControlMergeStatus(MergeStatus status) override { |
| merge_status_ = status; |
| return true; |
| } |
| bool IsOverlayfsSetup() const override { return false; } |
| bool IsRecovery() const override { return recovery_; } |
| bool SetSlotAsUnbootable(unsigned int slot) override { |
| unbootable_slots_.insert(slot); |
| return true; |
| } |
| bool IsTestDevice() const override { return true; } |
| bool IsFirstStageInit() const override { return first_stage_init_; } |
| std::unique_ptr<IImageManager> OpenImageManager() const override { |
| return IDeviceInfo::OpenImageManager("ota/test"); |
| } |
| android::dm::IDeviceMapper& GetDeviceMapper() override { |
| if (dm_) { |
| return *dm_; |
| } |
| return android::dm::DeviceMapper::Instance(); |
| } |
| |
| bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; } |
| |
| void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; } |
| void set_fake_super(const std::string& path) { |
| opener_ = std::make_unique<TestPartitionOpener>(path); |
| } |
| void set_recovery(bool value) { recovery_ = value; } |
| void set_first_stage_init(bool value) { first_stage_init_ = value; } |
| void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; } |
| |
| MergeStatus merge_status() const { return merge_status_; } |
| |
| private: |
| std::string slot_suffix_ = "_a"; |
| std::unique_ptr<TestPartitionOpener> opener_; |
| MergeStatus merge_status_; |
| bool recovery_ = false; |
| bool first_stage_init_ = false; |
| std::unordered_set<uint32_t> unbootable_slots_; |
| android::dm::IDeviceMapper* dm_ = nullptr; |
| }; |
| |
| class DeviceMapperWrapper : public android::dm::IDeviceMapper { |
| using DmDeviceState = android::dm::DmDeviceState; |
| using DmTable = android::dm::DmTable; |
| |
| public: |
| DeviceMapperWrapper() : impl_(android::dm::DeviceMapper::Instance()) {} |
| explicit DeviceMapperWrapper(android::dm::IDeviceMapper& impl) : impl_(impl) {} |
| |
| virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path, |
| const std::chrono::milliseconds& timeout_ms) override { |
| return impl_.CreateDevice(name, table, path, timeout_ms); |
| } |
| virtual DmDeviceState GetState(const std::string& name) const override { |
| return impl_.GetState(name); |
| } |
| virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) { |
| return impl_.LoadTableAndActivate(name, table); |
| } |
| virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) { |
| return impl_.GetTableInfo(name, table); |
| } |
| virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) { |
| return impl_.GetTableStatus(name, table); |
| } |
| virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) { |
| return impl_.GetDmDevicePathByName(name, path); |
| } |
| virtual bool GetDeviceString(const std::string& name, std::string* dev) { |
| return impl_.GetDeviceString(name, dev); |
| } |
| virtual bool DeleteDeviceIfExists(const std::string& name) { |
| return impl_.DeleteDeviceIfExists(name); |
| } |
| |
| private: |
| android::dm::IDeviceMapper& impl_; |
| }; |
| |
| class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher { |
| public: |
| SnapshotTestPropertyFetcher(const std::string& slot_suffix) { |
| using testing::Return; |
| ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix)); |
| ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _)) |
| .WillByDefault(Return(true)); |
| ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _)) |
| .WillByDefault(Return(false)); |
| ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true)); |
| } |
| |
| static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); } |
| |
| static void TearDown() { Reset("_a"); } |
| |
| private: |
| static void Reset(const std::string& slot_suffix) { |
| IPropertyFetcher::OverrideForTesting( |
| std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix)); |
| } |
| }; |
| |
| // Helper for error-spam-free cleanup. |
| void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name); |
| |
| // Write some random data to the given device. |
| // If expect_size is not specified, will write until reaching end of the device. |
| // Expect space of |path| is multiple of 4K. |
| bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt, |
| std::string* hash = nullptr); |
| bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr); |
| std::string HashSnapshot(ISnapshotWriter* writer); |
| |
| std::optional<std::string> GetHash(const std::string& path); |
| |
| // Add partitions and groups described by |manifest|. |
| AssertionResult FillFakeMetadata(MetadataBuilder* builder, const DeltaArchiveManifest& manifest, |
| const std::string& suffix); |
| |
| // In the update package metadata, set a partition with the given size. |
| void SetSize(PartitionUpdate* partition_update, uint64_t size); |
| |
| // Get partition size from update package metadata. |
| uint64_t GetSize(PartitionUpdate* partition_update); |
| |
| // Util class for test cases on low space scenario. These tests assumes image manager |
| // uses /data as backup device. |
| class LowSpaceUserdata { |
| public: |
| // Set the maximum free space allowed for this test. If /userdata has more space than the given |
| // number, a file is allocated to consume space. |
| AssertionResult Init(uint64_t max_free_space); |
| |
| uint64_t free_space() const; |
| uint64_t available_space() const; |
| uint64_t bsize() const; |
| |
| private: |
| AssertionResult ReadUserdataStats(); |
| |
| static constexpr const char* kUserDataDevice = "/data"; |
| std::unique_ptr<TemporaryFile> big_file_; |
| bool initialized_ = false; |
| uint64_t free_space_ = 0; |
| uint64_t available_space_ = 0; |
| uint64_t bsize_ = 0; |
| }; |
| |
| bool IsVirtualAbEnabled(); |
| |
| #define SKIP_IF_NON_VIRTUAL_AB() \ |
| do { \ |
| if (!IsVirtualAbEnabled()) GTEST_SKIP() << "Test for Virtual A/B devices only"; \ |
| } while (0) |
| |
| #define RETURN_IF_NON_VIRTUAL_AB_MSG(msg) \ |
| do { \ |
| if (!IsVirtualAbEnabled()) { \ |
| std::cerr << (msg); \ |
| return; \ |
| } \ |
| } while (0) |
| |
| #define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("") |
| |
| } // namespace snapshot |
| } // namespace android |