blob: 4c277222292a5c7804b01df7928eced2d2cb4227 [file] [log] [blame]
// Copyright 2019 The Chromium 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 "chrome/common/conflicts/remote_module_watcher_win.h"
#include <windows.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/common/conflicts/module_event_sink_win.mojom.h"
#include "content/public/common/service_names.mojom.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/cpp/service_binding.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class RemoteModuleWatcherTest : public testing::Test,
public service_manager::Service,
public mojom::ModuleEventSink {
public:
RemoteModuleWatcherTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
base::test::ScopedTaskEnvironment::NowSource::
MAIN_THREAD_MOCK_TIME),
service_binding_(this,
test_connector_factory_.RegisterInstance(
content::mojom::kSystemServiceName)),
binding_(this) {}
~RemoteModuleWatcherTest() override = default;
// service_manager::Service:
void OnStart() override {
registry_.AddInterface(base::BindRepeating(
&RemoteModuleWatcherTest::BindModuleEventSinkRequest,
base::Unretained(this)));
}
void OnBindInterface(const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
registry_.BindInterface(interface_name, std::move(interface_pipe));
}
// mojom::ModuleEventSink:
void OnModuleEvents(
const std::vector<uint64_t>& module_load_addresses) override {
module_event_count_ += module_load_addresses.size();
}
// Returns a connector that may be used to connect to a ModuleEventSink
// implementation.
service_manager::Connector* GetConnector() {
return test_connector_factory_.GetDefaultConnector();
}
void LoadModule() {
if (module_handle_)
return;
// This module should not be a static dependency of the unit-test
// executable, but should be a build-system dependency or a module that is
// present on any Windows machine.
static constexpr wchar_t kModuleName[] = L"conflicts_dll.dll";
// The module should not already be loaded.
ASSERT_FALSE(::GetModuleHandle(kModuleName));
// It should load successfully.
module_handle_ = ::LoadLibrary(kModuleName);
ASSERT_TRUE(module_handle_);
}
void UnloadModule() {
if (!module_handle_)
return;
::FreeLibrary(module_handle_);
module_handle_ = nullptr;
}
// Runs the task scheduler until no tasks are running.
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
void FastForwardByIdleDelay() {
scoped_task_environment_.FastForwardBy(RemoteModuleWatcher::kIdleDelay);
}
HMODULE module_handle() { return module_handle_; }
int module_event_count() { return module_event_count_; }
private:
void BindModuleEventSinkRequest(mojom::ModuleEventSinkRequest request) {
binding_.Bind(std::move(request));
}
// Must be first.
base::test::ScopedTaskEnvironment scoped_task_environment_;
service_manager::TestConnectorFactory test_connector_factory_;
// Used to implement |service_manager::Service|.
service_manager::ServiceBinding service_binding_;
service_manager::BinderRegistry registry_;
// Binds a ModuleEventSinkRequest to this implementation of ModuleEventSink.
mojo::Binding<mojom::ModuleEventSink> binding_;
// Holds a handle to a loaded module.
HMODULE module_handle_ = nullptr;
// Total number of module events seen.
int module_event_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(RemoteModuleWatcherTest);
};
} // namespace
TEST_F(RemoteModuleWatcherTest, ModuleEvents) {
auto remote_module_watcher = RemoteModuleWatcher::Create(
base::ThreadTaskRunnerHandle::Get(), GetConnector());
// Wait until the watcher is initialized and events for already loaded modules
// are received.
RunUntilIdle();
// Now wait for the timer used to batch events to expire.
FastForwardByIdleDelay();
EXPECT_GT(module_event_count(), 0);
// Dynamically load a module and ensure a notification is received for it.
int previous_module_event_count = module_event_count();
LoadModule();
FastForwardByIdleDelay();
EXPECT_GT(module_event_count(), previous_module_event_count);
UnloadModule();
// Destroy the module watcher.
remote_module_watcher = nullptr;
RunUntilIdle();
// Load the module and ensure no notification is received this time.
previous_module_event_count = module_event_count();
LoadModule();
FastForwardByIdleDelay();
EXPECT_EQ(module_event_count(), previous_module_event_count);
UnloadModule();
}