blob: d968285302ca131a5d3174b34ca45fd7f4569560 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/ld/testing/mock-loader-service.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/elfldltl/testing/get-test-data.h>
#include <lib/fit/defer.h>
#include <zircon/dlfcn.h>
#include <filesystem>
#include <gmock/gmock.h>
namespace ld::testing {
namespace {
using ::testing::Return;
} // namespace
// A mock server implementation serving the fuchsia.ldsvc.Loader protocol. When
// it receives a request, it will invoke the associated MOCK_METHOD. If the
// test caller did not call Expect* for the request before it is made, then
// the MockServer will fail the test.
class MockLoaderService::MockServer : public fidl::WireServer<fuchsia_ldsvc::Loader> {
public:
MockServer() = default;
~MockServer() {
if (backing_loop_) {
backing_loop_->Shutdown();
}
}
void Init(fidl::ServerEnd<fuchsia_ldsvc::Loader> server) {
backing_loop_.emplace(&kAsyncLoopConfigNoAttachToCurrentThread);
zx_status_t status = backing_loop_->StartThread("MockLoaderService");
ASSERT_EQ(status, ZX_OK) << zx_status_get_string(status);
fidl::BindServer(backing_loop_->dispatcher(), std::move(server), this);
}
// Note the mocked methods take std::string, though the Expect* wrappers take
// std::string_view. The std::string_view passed to the wrappers is not
// guaranteed to point to memory that's valid after the call, so they copy
// into a fresh std::string for EXPECT_CALL; those stay live in the mock.
MOCK_METHOD(zx::result<zx::vmo>, MockLoadObject, (std::string));
MOCK_METHOD(zx::result<>, MockConfig, (std::string));
private:
// The fidl::WireServer<fuchsia_ldsvc::Loader> implementation, each FIDL
// method will call into its associated gMock method when invoked.
// Done is not used in ld tests.
void Done(DoneCompleter::Sync& completer) override { ADD_FAILURE() << "unexpected Done call"; }
void LoadObject(LoadObjectRequestView request, LoadObjectCompleter::Sync& completer) override {
auto result = MockLoadObject(std::string(request->object_name.get()));
completer.Reply(result.status_value(), std::move(result).value_or(zx::vmo()));
}
void Config(ConfigRequestView request, ConfigCompleter::Sync& completer) override {
auto result = MockConfig(std::string(request->config.get()));
completer.Reply(result.status_value());
}
// Clone is not used in ld tests.
void Clone(CloneRequestView request, CloneCompleter::Sync& completer) override {
ADD_FAILURE() << "unexpected Clone call";
}
std::optional<async::Loop> backing_loop_;
};
MockLoaderService::MockLoaderService() = default;
MockLoaderService::~MockLoaderService() = default;
void MockLoaderService::Init() {
mock_server_ = std::make_unique<::testing::StrictMock<MockServer>>();
auto endpoints = fidl::Endpoints<fuchsia_ldsvc::Loader>::Create();
ASSERT_NO_FATAL_FAILURE(mock_server_->Init(std::move(endpoints.server)));
mock_client_ = std::move(endpoints.client);
}
void MockLoaderService::ExpectLoadObject(std::string_view name,
zx::result<zx::vmo> expected_result) {
EXPECT_CALL(*mock_server_, MockLoadObject(std::string{name}))
.WillOnce(Return(std::move(expected_result)));
}
void MockLoaderService::ExpectConfig(std::string_view name, zx::result<> expected_result) {
EXPECT_CALL(*mock_server_, MockConfig(std::string{name})).WillOnce(Return(expected_result));
}
void MockLoaderServiceForTest::Needed(std::initializer_list<std::string_view> names) {
for (std::string_view name : names) {
ExpectDependency(name);
}
}
void MockLoaderServiceForTest::Needed(
std::initializer_list<std::pair<std::string_view, bool>> name_found_pairs) {
for (auto [name, found] : name_found_pairs) {
if (found) {
ExpectDependency(name);
} else {
ExpectMissing(name);
}
}
}
void MockLoaderServiceForTest::ExpectLoadObject(std::string_view name,
zx::result<zx::vmo> expected_result) {
ASSERT_NO_FATAL_FAILURE(ReadyMock());
mock_loader_->ExpectLoadObject(name, std::move(expected_result));
}
void MockLoaderServiceForTest::ExpectLoadObject(std::string_view name, zx::vmo vmo) {
ASSERT_TRUE(vmo);
ExpectLoadObject(name, zx::ok(std::move(vmo)));
}
void MockLoaderServiceForTest::ExpectDependency(std::string_view name) {
ExpectLoadObject(name, GetDepVmo(name));
}
void MockLoaderServiceForTest::ExpectRootModule(std::string_view name) {
ExpectLoadObject(name, GetRootModuleVmo(name));
}
void MockLoaderServiceForTest::ExpectMissing(std::string_view name) {
ExpectLoadObject(name, zx::error{ZX_ERR_NOT_FOUND});
}
void MockLoaderServiceForTest::ExpectConfig(std::string_view config) {
ASSERT_NO_FATAL_FAILURE(ReadyMock());
mock_loader_->ExpectConfig(config, zx::ok());
}
zx::channel MockLoaderServiceForTest::TakeLdsvc() {
zx::channel ldsvc;
if (mock_loader_) {
ldsvc = mock_loader_->client().TakeChannel();
}
return ldsvc;
}
zx::unowned_channel MockLoaderServiceForTest::BorrowLdsvc() {
zx::unowned_channel ldsvc;
if (mock_loader_) {
ldsvc = mock_loader_->client().channel().borrow();
}
return ldsvc;
}
void MockLoaderServiceForTest::CallWithLoaderInstalled(fit::function<void()> func) {
// Install the mock loader as the system loader.
auto mock_ldsvc = BorrowLdsvc();
ASSERT_TRUE(mock_ldsvc->is_valid());
// Restore the loader to the previous system loader at the close of function scope.
auto restore_system_ldsvc =
fit::defer([system_ldsvc = zx::channel{dl_set_loader_service(mock_ldsvc->get())}]() mutable {
dl_set_loader_service(system_ldsvc.release());
});
// Now that the mock loader service is installed, call the function.
func();
}
zx::vmo MockLoaderServiceForTest::GetDepVmo(std::string_view name) {
// TODO(https://fxbug.dev/335737373): use a more direct means to look up the file.
const std::string path = std::filesystem::path("test") / "lib" / LD_TEST_LIBPREFIX / name;
return elfldltl::testing::GetTestLibVmo(path);
}
zx::vmo MockLoaderServiceForTest::GetRootModuleVmo(std::string_view name) {
const std::string path = std::filesystem::path("test") / "lib" / name;
return elfldltl::testing::GetTestLibVmo(path);
}
void MockLoaderServiceForTest::ReadyMock() {
if (!mock_loader_) {
mock_loader_ = std::make_unique<MockLoaderService>();
ASSERT_NO_FATAL_FAILURE(mock_loader_->Init());
}
}
} // namespace ld::testing