snapuserd: Add a harness to run tests without dm-user specific code.
This patch adds an abstraction layer around Tempdevice (which wraps
device-mapper), and a layer to replace hardcoding of DmUserBlockServer.
The only implementation of the new layer, currently, is for dm-user.
However this will allow the harness to run with a backend chosen at
runtime, making testing on the host or of ublk much easier.
Bug: 288273605
Test: snapuserd_test
Change-Id: I8735ef6c373f3e5c5cdf3df461668ddd8e551f63
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 40dcc2a..2368f8f 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -219,6 +219,7 @@
"libsnapshot_cow_defaults",
],
srcs: [
+ "testing/dm_user_harness.cpp",
"user-space-merge/snapuserd_test.cpp",
],
shared_libs: [
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
new file mode 100644
index 0000000..7cadf25
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 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 "dm_user_harness.h"
+
+#include <fcntl.h>
+
+#include <android-base/file.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
+#include <snapuserd/dm_user_block_server.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+DmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}
+
+const std::string& DmUserDevice::GetPath() {
+ return dev_->path();
+}
+
+bool DmUserDevice::Destroy() {
+ return dev_->Destroy();
+}
+
+DmUserTestHarness::DmUserTestHarness() {
+ block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
+}
+
+std::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) {
+ android::dm::DmTable dmuser_table;
+ dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);
+ auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);
+ if (!dev->valid()) {
+ return nullptr;
+ }
+
+ auto misc_device = "/dev/dm-user/" + misc_name;
+ if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {
+ return nullptr;
+ }
+
+ return std::make_unique<DmUserDevice>(std::move(dev));
+}
+
+IBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {
+ return block_server_factory_.get();
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
new file mode 100644
index 0000000..cf26bed
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 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 <android-base/unique_fd.h>
+
+#include "harness.h"
+#include "temp_device.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class DmUserBlockServerFactory;
+
+class DmUserDevice final : public IUserDevice {
+ public:
+ explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);
+ const std::string& GetPath() override;
+ bool Destroy() override;
+
+ private:
+ std::unique_ptr<Tempdevice> dev_;
+};
+
+class DmUserTestHarness final : public ITestHarness {
+ public:
+ DmUserTestHarness();
+
+ std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) override;
+ IBlockServerFactory* GetBlockServerFactory() override;
+ bool HasUserDevice() override { return true; }
+
+ private:
+ std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.h b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
new file mode 100644
index 0000000..c0d528f
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+
+namespace android {
+namespace snapshot {
+
+// Interface for a "block driver in userspace" device.
+class IUserDevice {
+ public:
+ virtual ~IUserDevice() {}
+ virtual const std::string& GetPath() = 0;
+ virtual bool Destroy() = 0;
+};
+
+class ITestHarness {
+ public:
+ virtual ~ITestHarness() {}
+ virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) = 0;
+ virtual IBlockServerFactory* GetBlockServerFactory() = 0;
+ virtual bool HasUserDevice() = 0;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 157925d..4ea1d5d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -41,6 +41,7 @@
#include <storage_literals/storage_literals.h>
#include "handler_manager.h"
#include "snapuserd_core.h"
+#include "testing/dm_user_harness.h"
#include "testing/temp_device.h"
DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
@@ -75,10 +76,9 @@
static const uint64_t kSectorSize = 512;
protected:
- void SetUp() override {}
+ void SetUp() override;
void TearDown() override { Shutdown(); }
- private:
void SetupImpl();
void SimulateDaemonRestart();
@@ -94,10 +94,10 @@
void InitCowDevice();
void SetDeviceControlName();
void InitDaemon();
- void CreateDmUserDevice();
+ void CreateUserDevice();
unique_ptr<LoopDevice> base_loop_;
- unique_ptr<Tempdevice> dmuser_dev_;
+ unique_ptr<IUserDevice> dmuser_dev_;
std::string system_device_ctrl_name_;
std::string system_device_name_;
@@ -112,6 +112,7 @@
size_t size_ = 100_MiB;
int cow_num_sectors_;
int total_base_size_;
+ std::unique_ptr<ITestHarness> harness_;
};
static unique_fd CreateTempFile(const std::string& name, size_t size) {
@@ -132,6 +133,10 @@
return fd;
}
+void SnapuserdTest::SetUp() {
+ harness_ = std::make_unique<DmUserTestHarness>();
+}
+
void SnapuserdTest::Shutdown() {
ASSERT_TRUE(dmuser_dev_->Destroy());
@@ -173,7 +178,7 @@
bool SnapuserdTest::SetupDaemon() {
SetDeviceControlName();
- CreateDmUserDevice();
+ CreateUserDevice();
InitCowDevice();
InitDaemon();
@@ -206,7 +211,7 @@
}
void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
- unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
+ unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
ASSERT_GE(fd, 0);
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
@@ -535,9 +540,8 @@
use_iouring = false;
}
- DmUserBlockServerFactory factory;
-
- auto opener = factory.CreateOpener(system_device_ctrl_name_);
+ auto factory = harness_->GetBlockServerFactory();
+ auto opener = factory->CreateOpener(system_device_ctrl_name_);
auto handler =
handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_loop_->device(),
base_loop_->device(), opener, 1, use_iouring, false);
@@ -560,7 +564,7 @@
system_device_ctrl_name_ = system_device_name_ + "-ctrl";
}
-void SnapuserdTest::CreateDmUserDevice() {
+void SnapuserdTest::CreateUserDevice() {
unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
ASSERT_TRUE(fd > 0);
@@ -569,17 +573,9 @@
cow_num_sectors_ = dev_sz >> 9;
- DmTable dmuser_table;
- ASSERT_TRUE(dmuser_table.AddTarget(
- std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
+ dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
+ cow_num_sectors_);
+ ASSERT_NE(dmuser_dev_, nullptr);
}
void SnapuserdTest::InitDaemon() {
@@ -603,7 +599,7 @@
SetDeviceControlName();
- CreateDmUserDevice();
+ CreateUserDevice();
InitCowDevice();
InitDaemon();
@@ -632,7 +628,7 @@
Shutdown();
std::this_thread::sleep_for(500ms);
SetDeviceControlName();
- CreateDmUserDevice();
+ CreateUserDevice();
InitCowDevice();
InitDaemon();
}
@@ -695,6 +691,9 @@
}
TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
ASSERT_TRUE(SetupDefault());
// I/O before merge
ReadSnapshotDeviceAndValidate();
@@ -707,6 +706,9 @@
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
ASSERT_TRUE(SetupDefault());
// Issue I/O before merge begins
std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
@@ -717,6 +719,9 @@
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
ASSERT_TRUE(SetupDefault());
// Start the merge
StartMerge();