Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0841152 authored by David Anderson's avatar David Anderson
Browse files

libsnapshot: Unify vts_libsnapshot_test harness.

A few major changes in this patch:

The userspace tests have been folded back into the main test file, with
some runtime checks to gate any dependent assertions.

The default test mode is to test using the device's native
configuration. However, there is now a -force_config flag with three
options:
 - "dmsnap" to test the old dm-snapshot code path. This is for legacy
   VAB (no compression, no snapuserd).
 - "vab" to test userspace merges.
 - "vabc" to test userspace merges with compression.

Note that "dmsnap" + "snapuserd" is not possible to test anymore, as the
code will soon be deleted.

The test suite uses the force_config flag to propagate settings to
SnapshotManager/snapuserd.

In addition, there is a new test wrapper, "run_snapshot_tests.sh". This
shell script can be run on the host or device, and will invoke
vts_libsnapshot_test. Once all three configurations are passing, they
will be added to this harness, and presubmit will be changed to run the
harness instead. The default mode will continue to be used for VTS.

Bug: 208944665
Test: vts_libsnapshot_test
Change-Id: I0bb0fdc936ce748c5fbe8ec2acb1eda6c16b11ae
parent d2f8d514
Loading
Loading
Loading
Loading
+10 −49
Original line number Diff line number Diff line
@@ -236,52 +236,7 @@ cc_defaults {
        "libbrotli",
        "libc++fs",
        "libfs_mgr_binder",
        "libgsi",
        "libgmock",
        "liblp",
        "libsnapshot",
        "libsnapshot_cow",
        "libsnapshot_test_helpers",
        "libsparse",
    ],
    header_libs: [
        "libstorage_literals_headers",
    ],
    test_suites: [
        "vts",
        "device-tests"
    ],
    test_options: {
        min_shipping_api_level: 29,
    },
    auto_gen_config: true,
    require_root: true,
}

cc_defaults {
    name: "userspace_snapshot_test_defaults",
    defaults: ["libsnapshot_defaults"],
    srcs: [
        "partition_cow_creator_test.cpp",
        "snapshot_metadata_updater_test.cpp",
        "snapshot_reader_test.cpp",
        "userspace_snapshot_test.cpp",
        "snapshot_writer_test.cpp",
    ],
    shared_libs: [
        "libbinder",
        "libcrypto",
        "libhidlbase",
        "libprotobuf-cpp-lite",
        "libutils",
        "libz",
    ],
    static_libs: [
        "android.hardware.boot@1.0",
        "android.hardware.boot@1.1",
        "libbrotli",
        "libc++fs",
        "libfs_mgr_binder",
        "libgflags",
        "libgsi",
        "libgmock",
        "liblp",
@@ -309,9 +264,15 @@ cc_test {
    defaults: ["libsnapshot_test_defaults"],
}

cc_test {
    name: "vts_userspace_snapshot_test",
    defaults: ["userspace_snapshot_test_defaults"],
sh_test {
    name: "run_snapshot_tests",
    src: "run_snapshot_tests.sh",
    test_suites: [
        "device-tests",
    ],
    required: [
        "vts_libsnapshot_test",
    ],
}

cc_binary {
+2 −1
Original line number Diff line number Diff line
@@ -793,7 +793,8 @@ class SnapshotManager final : public ISnapshotManager {

    // Helper of UpdateUsesCompression
    bool UpdateUsesCompression(LockedFile* lock);
    // Helper of UpdateUsesUsersnapshots
    // Locked and unlocked functions to test whether the current update uses
    // userspace snapshots.
    bool UpdateUsesUserSnapshots(LockedFile* lock);

    // Wrapper around libdm, with diagnostics.
+35 −0
Original line number Diff line number Diff line
#!/system/bin/sh

# Detect host or AOSP.
getprop ro.build.version.sdk > /dev/null 2>&1
if [ $? -eq 0 ]; then
    cmd_prefix=""
    local_root=""
else
    cmd_prefix="adb shell"
    local_root="${ANDROID_PRODUCT_OUT}"
    set -e
    set -x
    adb root
    adb sync data
    set +x
    set +e
fi

testpath64="/data/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"
testpath32="/data/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"
if [ -f "${local_root}/${testpath64}" ]; then
    testpath="${testpath64}"
elif [ -f "${local_root}/${testpath32}" ]; then
    testpath="${testpath32}"
else
    echo "ERROR: vts_libsnapshot_test not found." 1>&2
    echo "Make sure to build vts_libsnapshot_test or snapshot_tests first." 1>&2
    exit 1
fi

# Verbose, error on failure.
set -x
set -e

time ${cmd_prefix} ${testpath}
+127 −35
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <fs_mgr/file_wait.h>
#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
@@ -52,6 +53,8 @@
#include <libsnapshot/mock_device_info.h>
#include <libsnapshot/mock_snapshot.h>

DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");

namespace android {
namespace snapshot {

@@ -87,6 +90,8 @@ TestDeviceInfo* test_device = nullptr;
std::string fake_super;

void MountMetadata();
bool ShouldUseCompression();
bool ShouldUseUserspaceSnapshots();

class SnapshotTest : public ::testing::Test {
  public:
@@ -428,7 +433,7 @@ TEST_F(SnapshotTest, CreateSnapshot) {
    ASSERT_TRUE(AcquireLock());

    PartitionCowCreator cow_creator;
    cow_creator.compression_enabled = IsCompressionEnabled();
    cow_creator.compression_enabled = ShouldUseCompression();
    if (cow_creator.compression_enabled) {
        cow_creator.compression_algorithm = "gz";
    } else {
@@ -469,7 +474,7 @@ TEST_F(SnapshotTest, MapSnapshot) {
    ASSERT_TRUE(AcquireLock());

    PartitionCowCreator cow_creator;
    cow_creator.compression_enabled = IsCompressionEnabled();
    cow_creator.compression_enabled = ShouldUseCompression();

    static const uint64_t kDeviceSize = 1024 * 1024;
    SnapshotStatus status;
@@ -527,6 +532,8 @@ TEST_F(SnapshotTest, Merge) {
    std::unique_ptr<ISnapshotWriter> writer;
    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));

    bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());

    // Release the lock.
    lock_ = nullptr;

@@ -548,7 +555,11 @@ TEST_F(SnapshotTest, Merge) {
    // The device should have been switched to a snapshot-merge target.
    DeviceMapper::TargetInfo target;
    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
    if (userspace_snapshots) {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
    } else {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
    }

    // We should not be able to cancel an update now.
    ASSERT_FALSE(sm->CancelUpdate());
@@ -584,11 +595,13 @@ TEST_F(SnapshotTest, FirstStageMountAndMerge) {

    ASSERT_TRUE(AcquireLock());

    bool userspace_snapshots = init->UpdateUsesUserSnapshots(lock_.get());

    // Validate that we have a snapshot device.
    SnapshotStatus status;
    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
    ASSERT_EQ(status.state(), SnapshotState::CREATED);
    if (IsCompressionEnabled()) {
    if (ShouldUseCompression()) {
        ASSERT_EQ(status.compression_algorithm(), "gz");
    } else {
        ASSERT_EQ(status.compression_algorithm(), "none");
@@ -596,8 +609,12 @@ TEST_F(SnapshotTest, FirstStageMountAndMerge) {

    DeviceMapper::TargetInfo target;
    ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
    if (userspace_snapshots) {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
    } else {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
    }
}

TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
    ASSERT_TRUE(AcquireLock());
@@ -858,7 +875,7 @@ class SnapshotUpdateTest : public SnapshotTest {
        opener_ = std::make_unique<TestPartitionOpener>(fake_super);

        auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
        dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
        dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);

        // Create a fake update package metadata.
@@ -991,7 +1008,7 @@ class SnapshotUpdateTest : public SnapshotTest {
    }

    AssertionResult MapOneUpdateSnapshot(const std::string& name) {
        if (IsCompressionEnabled()) {
        if (ShouldUseCompression()) {
            std::unique_ptr<ISnapshotWriter> writer;
            return MapUpdateSnapshot(name, &writer);
        } else {
@@ -1001,7 +1018,7 @@ class SnapshotUpdateTest : public SnapshotTest {
    }

    AssertionResult WriteSnapshotAndHash(const std::string& name) {
        if (IsCompressionEnabled()) {
        if (ShouldUseCompression()) {
            std::unique_ptr<ISnapshotWriter> writer;
            auto res = MapUpdateSnapshot(name, &writer);
            if (!res) {
@@ -1169,7 +1186,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {

    // Initiate the merge and wait for it to be completed.
    ASSERT_TRUE(init->InitiateMerge());
    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
    {
        // We should have started in SECOND_PHASE since nothing shrinks.
        ASSERT_TRUE(AcquireLock());
@@ -1196,7 +1213,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
}

TEST_F(SnapshotUpdateTest, DuplicateOps) {
    if (!IsCompressionEnabled()) {
    if (!ShouldUseCompression()) {
        GTEST_SKIP() << "Compression-only test";
    }

@@ -1240,7 +1257,7 @@ TEST_F(SnapshotUpdateTest, DuplicateOps) {
// Test that shrinking and growing partitions at the same time is handled
// correctly in VABC.
TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
    if (!IsCompressionEnabled()) {
    if (!ShouldUseCompression()) {
        // b/179111359
        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
    }
@@ -1303,7 +1320,7 @@ TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {

    // Initiate the merge and wait for it to be completed.
    ASSERT_TRUE(init->InitiateMerge());
    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
    {
        // Check that the merge phase is FIRST_PHASE until at least one call
        // to ProcessUpdateState() occurs.
@@ -1320,11 +1337,21 @@ TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
    // Check that we used the correct types after rebooting mid-merge.
    DeviceMapper::TargetInfo target;
    ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));

    bool userspace_snapshots = init->UpdateUsesUserSnapshots();
    if (userspace_snapshots) {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
    } else {
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
    }

    // Complete the merge.
    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
@@ -1802,6 +1829,8 @@ TEST_F(SnapshotUpdateTest, MergeInFastboot) {

    ASSERT_TRUE(new_sm->FinishMergeInRecovery());

    ASSERT_TRUE(UnmapAll());

    auto mount = new_sm->EnsureMetadataMounted();
    ASSERT_TRUE(mount && mount->HasDevice());
    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
@@ -1894,6 +1923,8 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
    ASSERT_FALSE(test_device->IsSlotUnbootable(1));
    ASSERT_FALSE(test_device->IsSlotUnbootable(0));

    ASSERT_TRUE(UnmapAll());

    // Now reboot into new slot.
    test_device = new TestDeviceInfo(fake_super, "_b");
    auto init = NewManagerForFirstStageMount(test_device);
@@ -1922,8 +1953,8 @@ TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
        ASSERT_TRUE(AcquireLock());

        PartitionCowCreator cow_creator = {
                .compression_enabled = IsCompressionEnabled(),
                .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
                .compression_enabled = ShouldUseCompression(),
                .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
        };
        SnapshotStatus status;
        status.set_name("sys_a");
@@ -1955,6 +1986,8 @@ TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
    ASSERT_FALSE(test_device->IsSlotUnbootable(1));
    ASSERT_FALSE(test_device->IsSlotUnbootable(0));

    ASSERT_TRUE(UnmapAll());

    // Now reboot into new slot.
    test_device = new TestDeviceInfo(fake_super, "_b");
    auto init = NewManagerForFirstStageMount(test_device);
@@ -2017,7 +2050,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {

// Test for overflow bit after update
TEST_F(SnapshotUpdateTest, Overflow) {
    if (IsCompressionEnabled()) {
    if (ShouldUseCompression()) {
        GTEST_SKIP() << "No overflow bit set for userspace COWs";
    }

@@ -2152,7 +2185,7 @@ class AutoKill final {
};

TEST_F(SnapshotUpdateTest, DaemonTransition) {
    if (!IsCompressionEnabled()) {
    if (!ShouldUseCompression()) {
        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
    }

@@ -2178,14 +2211,30 @@ TEST_F(SnapshotUpdateTest, DaemonTransition) {
    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));

    bool userspace_snapshots = init->UpdateUsesUserSnapshots();

    if (userspace_snapshots) {
        ASSERT_EQ(access("/dev/dm-user/sys_b-init", F_OK), 0);
        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), -1);
    } else {
        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
    }

    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));

    // :TODO: this is a workaround to ensure the handler list stays empty. We
    // should make this test more like actual init, and spawn two copies of
    // snapuserd, given how many other tests we now have for normal snapuserd.
    if (userspace_snapshots) {
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-init"));
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-init"));
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-init"));

        // The control device should have been renamed.
        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-init", 10s));
        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), 0);
    } else {
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
@@ -2194,6 +2243,7 @@ TEST_F(SnapshotUpdateTest, DaemonTransition) {
        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
    }
}

TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
    AddOperationForPartitions();
@@ -2233,8 +2283,13 @@ TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
            },
            &path));

    bool userspace_snapshots = sm->UpdateUsesUserSnapshots();

    unique_fd fd;
    if (!userspace_snapshots) {
        // Hold sys_a open so it can't be unmapped.
    unique_fd fd(open(path.c_str(), O_RDONLY));
        fd.reset(open(path.c_str(), O_RDONLY));
    }

    // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
    // we should simply delete the old snapshots.
@@ -2253,6 +2308,11 @@ TEST_F(SnapshotUpdateTest, QueryStatusError) {
    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));

    if (sm->UpdateUsesUserSnapshots()) {
        GTEST_SKIP() << "Test does not apply to userspace snapshots";
    }

    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
    ASSERT_TRUE(UnmapAll());
@@ -2557,21 +2617,53 @@ void SnapshotTestEnvironment::TearDown() {
    }
}

bool ShouldUseUserspaceSnapshots() {
    if (FLAGS_force_config == "dmsnap") {
        return false;
    }
    if (!FLAGS_force_config.empty()) {
        return true;
    }
    return IsUserspaceSnapshotsEnabled();
}

bool ShouldUseCompression() {
    if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
        return false;
    }
    if (FLAGS_force_config == "vabc") {
        return true;
    }
    return IsCompressionEnabled();
}

}  // namespace snapshot
}  // namespace android

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
    gflags::ParseCommandLineFlags(&argc, &argv, false);

    android::base::SetProperty("ctl.stop", "snapuserd");

    std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
    if (configs.count(FLAGS_force_config) == 0) {
        std::cerr << "Unexpected force_config argument\n";
        return 1;
    }

    if (FLAGS_force_config == "dmsnap") {
        if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
            return testing::AssertionFailure()
                   << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
        }
    }

    int ret = RUN_ALL_TESTS();

    if (FLAGS_force_config == "dmsnap") {
        android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
    }
    return ret;
}
+0 −2519

File deleted.

Preview size limit exceeded, changes collapsed.