snapuserd: Add an ITestHarness implementation for hosts.
This adds an implementation of ITestHarness suitable for running tests
on the host. IUserDevice and ProcessRequests are implemented just enough
for shutdown of ReadWorker to work. Most snapuserd tests are focused on
merging, and do not need a ReadWorker accepting requests.
Bug: 288273605
Test: snapuserd_test
Change-Id: I00cf6ec941fb6423290f7a299e5321adea7d8919
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index fc260b6..f5b87e0 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -222,6 +222,7 @@
srcs: [
"testing/dm_user_harness.cpp",
"testing/harness.cpp",
+ "testing/host_harness.cpp",
"user-space-merge/snapuserd_test.cpp",
],
shared_libs: [
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
new file mode 100644
index 0000000..bb90e6d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
@@ -0,0 +1,107 @@
+// 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 "host_harness.h"
+
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+void TestBlockServerQueue::WaitForShutdown() {
+ std::unique_lock lock(m_);
+ if (shutdown_) {
+ return;
+ }
+ cv_.wait(lock, [this]() -> bool { return shutdown_; });
+}
+
+void TestBlockServerQueue::Shutdown() {
+ std::unique_lock lock(m_);
+ shutdown_ = true;
+ cv_.notify_all();
+}
+
+TestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+bool TestBlockServer::ProcessRequests() {
+ queue_->WaitForShutdown();
+ return false;
+}
+
+void* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+ std::string buffer(size, '\0');
+ buffered_.emplace_back(std::move(buffer), to_write);
+ return buffered_.back().first.data();
+}
+
+bool TestBlockServer::SendBufferedIo() {
+ for (const auto& [data, to_write] : buffered_) {
+ sent_io_ += data.substr(0, to_write);
+ }
+ buffered_.clear();
+ return true;
+}
+
+TestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+std::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {
+ return std::make_unique<TestBlockServer>(queue_, misc_name_);
+}
+
+std::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(
+ const std::string& misc_name) {
+ if (queues_.count(misc_name)) {
+ LOG(ERROR) << "Cannot create opener for " << misc_name << ", already exists";
+ return nullptr;
+ }
+ auto queue = std::make_shared<TestBlockServerQueue>();
+ queues_.emplace(misc_name, queue);
+ return std::make_shared<TestBlockServerOpener>(queue, misc_name);
+}
+
+bool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {
+ auto iter = queues_.find(misc_name);
+ if (iter == queues_.end()) {
+ LOG(ERROR) << "Cannot delete queue " << misc_name << ", not found";
+ return false;
+ }
+ iter->second->Shutdown();
+ queues_.erase(iter);
+ return true;
+}
+
+HostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)
+ : factory_(factory), misc_name_(misc_name) {}
+
+bool HostUserDevice::Destroy() {
+ return factory_->DeleteQueue(misc_name_);
+}
+
+std::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,
+ const std::string& misc_name,
+ uint64_t) {
+ return std::make_unique<HostUserDevice>(&factory_, misc_name);
+}
+
+IBlockServerFactory* HostTestHarness::GetBlockServerFactory() {
+ return &factory_;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
new file mode 100644
index 0000000..e5c6985
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
@@ -0,0 +1,104 @@
+// 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 <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "harness.h"
+
+namespace android {
+namespace snapshot {
+
+class TestBlockServerQueue final {
+ public:
+ void WaitForShutdown();
+ void Shutdown();
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool shutdown_ = false;
+};
+
+class TestBlockServer final : public IBlockServer {
+ public:
+ TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);
+ bool ProcessRequests() override;
+ void* GetResponseBuffer(size_t size, size_t to_write) override;
+ bool SendBufferedIo() override;
+
+ std::string&& sent_io() { return std::move(sent_io_); }
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+ std::string sent_io_;
+ std::vector<std::pair<std::string, size_t>> buffered_;
+};
+
+class TestBlockServerOpener final : public IBlockServerOpener {
+ public:
+ TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name);
+ std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) override;
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+};
+
+class TestBlockServerFactory final : public IBlockServerFactory {
+ public:
+ std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+ bool DeleteQueue(const std::string& misc_name);
+
+ private:
+ std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;
+};
+
+class TestBlockServerFactory;
+
+class HostUserDevice final : public IUserDevice {
+ public:
+ HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);
+ const std::string& GetPath() override { return empty_path_; }
+ bool Destroy();
+
+ private:
+ TestBlockServerFactory* factory_;
+ std::string misc_name_;
+ std::string empty_path_;
+};
+
+class HostTestHarness final : public ITestHarness {
+ public:
+ 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 false; }
+
+ private:
+ TestBlockServerFactory factory_;
+};
+
+} // namespace snapshot
+} // namespace android