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
This commit is contained in:
parent
384b22ce58
commit
fe032d0391
5 changed files with 197 additions and 23 deletions
|
|
@ -219,6 +219,7 @@ cc_test {
|
|||
"libsnapshot_cow_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"testing/dm_user_harness.cpp",
|
||||
"user-space-merge/snapuserd_test.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
|
|
|
|||
67
fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
Normal file
67
fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
Normal file
|
|
@ -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
|
||||
54
fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
Normal file
54
fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
Normal file
|
|
@ -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
|
||||
47
fs_mgr/libsnapshot/snapuserd/testing/harness.h
Normal file
47
fs_mgr/libsnapshot/snapuserd/testing/harness.h
Normal file
|
|
@ -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
|
||||
|
|
@ -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 @@ class SnapuserdTest : public ::testing::Test {
|
|||
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 @@ class SnapuserdTest : public ::testing::Test {
|
|||
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 @@ class SnapuserdTest : public ::testing::Test {
|
|||
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 @@ static unique_fd CreateTempFile(const std::string& name, size_t size) {
|
|||
return fd;
|
||||
}
|
||||
|
||||
void SnapuserdTest::SetUp() {
|
||||
harness_ = std::make_unique<DmUserTestHarness>();
|
||||
}
|
||||
|
||||
void SnapuserdTest::Shutdown() {
|
||||
ASSERT_TRUE(dmuser_dev_->Destroy());
|
||||
|
||||
|
|
@ -173,7 +178,7 @@ bool SnapuserdTest::SetupCopyOverlap_2() {
|
|||
bool SnapuserdTest::SetupDaemon() {
|
||||
SetDeviceControlName();
|
||||
|
||||
CreateDmUserDevice();
|
||||
CreateUserDevice();
|
||||
InitCowDevice();
|
||||
InitDaemon();
|
||||
|
||||
|
|
@ -206,7 +211,7 @@ void SnapuserdTest::CreateBaseDevice() {
|
|||
}
|
||||
|
||||
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 @@ void SnapuserdTest::InitCowDevice() {
|
|||
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 @@ void SnapuserdTest::SetDeviceControlName() {
|
|||
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 @@ void SnapuserdTest::CreateDmUserDevice() {
|
|||
|
||||
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 @@ void SnapuserdTest::SetupImpl() {
|
|||
|
||||
SetDeviceControlName();
|
||||
|
||||
CreateDmUserDevice();
|
||||
CreateUserDevice();
|
||||
InitCowDevice();
|
||||
InitDaemon();
|
||||
|
||||
|
|
@ -632,7 +628,7 @@ void SnapuserdTest::SimulateDaemonRestart() {
|
|||
Shutdown();
|
||||
std::this_thread::sleep_for(500ms);
|
||||
SetDeviceControlName();
|
||||
CreateDmUserDevice();
|
||||
CreateUserDevice();
|
||||
InitCowDevice();
|
||||
InitDaemon();
|
||||
}
|
||||
|
|
@ -695,6 +691,9 @@ void SnapuserdTest::MergeInterrupt() {
|
|||
}
|
||||
|
||||
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_IO_TEST) {
|
|||
}
|
||||
|
||||
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) {
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue