Chromium Code Reviews| Index: chrome/browser/conflicts/module_database_win_unittest.cc |
| diff --git a/chrome/browser/conflicts/module_database_win_unittest.cc b/chrome/browser/conflicts/module_database_win_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..65a9226c09ef1161bb6b29b5097400559909cbba |
| --- /dev/null |
| +++ b/chrome/browser/conflicts/module_database_win_unittest.cc |
| @@ -0,0 +1,237 @@ |
| +// Copyright 2016 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/browser/conflicts/module_database_win.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/run_loop.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/threading/simple_thread.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace { |
| + |
| +// A simple mechanism for running a single task on a separate thread. |
| +class SingleTaskRunner : public base::SimpleThread { |
| + public: |
| + explicit SingleTaskRunner(const base::Closure& task) |
| + : base::SimpleThread("SingleTaskRunner"), task_(task) {} |
| + |
| + // Runs the provided task and exits. |
| + void Run() override { task_.Run(); } |
| + |
| + private: |
| + base::Closure task_; |
| + DISALLOW_COPY_AND_ASSIGN(SingleTaskRunner); |
| +}; |
| + |
| +// Launches a thread and runs a single task on it. |
| +void RunTask(const base::Closure& task) { |
| + SingleTaskRunner task_runner(task); |
| + task_runner.Start(); |
| + task_runner.Join(); |
| +} |
| + |
| +// Factory for mojom::ModuleEvents. |
| +mojom::ModuleEvent ModuleEvent(mojom::ModuleEventType type, |
| + const char* path, |
| + uint64_t address, |
| + uint32_t size) { |
| + mojom::ModuleEvent event; |
| + event.event_type = type; |
| + event.module_path = std::string(path); |
| + event.load_address = address; |
| + event.size = size; |
| + return event; |
| +} |
| + |
| +const char kDummyDll[] = "dummy.dll"; |
| +const char kFooDll[] = "foo.dll"; |
| + |
| +} // namespace |
| + |
| +class ModuleDatabaseTest : public testing::Test { |
| + public: |
|
grt (UTC plus 2)
2016/12/15 09:07:12
nit: protected
chrisha
2016/12/19 20:15:37
Done.
|
| + ModuleDatabaseTest() |
| + : message_loop_(new base::MessageLoop), |
| + module_database_(new ModuleDatabase(message_loop_->task_runner())) {} |
| + |
| + void RunLoopUntilIdle() { |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
| + } |
| + |
| + const ModuleDatabase::ModuleSet& modules() { |
| + return module_database_->modules_; |
| + } |
| + |
| + const ModuleDatabase::ProcessSet& processes() { |
| + return module_database_->processes_; |
| + } |
| + |
| + static uint32_t ProcessTypeToBit(content::ProcessType process_type) { |
| + return ModuleDatabase::ProcessTypeToBit(process_type); |
| + } |
| + |
| + std::unique_ptr<base::MessageLoop> message_loop_; |
| + std::unique_ptr<ModuleDatabase> module_database_; |
| +}; |
| + |
| +TEST_F(ModuleDatabaseTest, TasksAreBounced) { |
| + // Run a task on the current thread. This should not be bounced, so no |
| + // task should be scheduled on the task runner. |
| + module_database_->OnProcessStarted(1234, content::PROCESS_TYPE_BROWSER); |
| + EXPECT_TRUE(message_loop_->IsIdleForTesting()); |
| + module_database_->OnModuleEvent( |
| + 1234, ModuleEvent(mojom::ModuleEventType::MODULE_LOADED, kDummyDll, |
| + 0xBAADF00D, 100 * 4096)); |
| + EXPECT_TRUE(message_loop_->IsIdleForTesting()); |
| + module_database_->OnProcessEnded(1234); |
| + EXPECT_TRUE(message_loop_->IsIdleForTesting()); |
| + |
| + // Run similar tasks on another thread. These should be bounced. |
| + RunTask(base::Bind(&ModuleDatabase::OnProcessStarted, |
| + base::Unretained(module_database_.get()), 2345, |
| + content::PROCESS_TYPE_BROWSER)); |
| + EXPECT_FALSE(message_loop_->IsIdleForTesting()); |
| + RunLoopUntilIdle(); |
| + |
| + RunTask(base::Bind(&ModuleDatabase::OnModuleEvent, |
| + base::Unretained(module_database_.get()), 2345, |
| + ModuleEvent(mojom::ModuleEventType::MODULE_LOADED, |
| + kDummyDll, 0xDEADBEEF, 10 * 4096))); |
| + EXPECT_FALSE(message_loop_->IsIdleForTesting()); |
| + RunLoopUntilIdle(); |
| + |
| + RunTask(base::Bind(&ModuleDatabase::OnProcessEnded, |
| + base::Unretained(module_database_.get()), 2345)); |
| + EXPECT_FALSE(message_loop_->IsIdleForTesting()); |
| + RunLoopUntilIdle(); |
| +} |
| + |
| +TEST_F(ModuleDatabaseTest, DatabaseIsConsistent) { |
| + EXPECT_EQ(0u, modules().size()); |
| + EXPECT_EQ(0u, processes().size()); |
| + |
| + // Start a process. |
| + module_database_->OnProcessStarted(1234, content::PROCESS_TYPE_BROWSER); |
| + EXPECT_EQ(0u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + auto p1 = processes().begin(); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->process_type); |
| + EXPECT_EQ(0u, p1->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p1->unloaded_module_ids.size()); |
| + |
| + // Indicate an already loaded module. |
| + module_database_->OnModuleEvent( |
| + 1234, ModuleEvent(mojom::ModuleEventType::MODULE_ALREADY_LOADED, |
| + kDummyDll, 0xBAADF00D, 100 * 4096)); |
| + EXPECT_EQ(1u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + |
| + // Ensure that the process and module sets are up to date. |
| + auto m1 = modules().begin(); |
| + EXPECT_EQ(base::UTF8ToWide(kDummyDll), m1->module_path.value()); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m1->process_types); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->process_type); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p1->unloaded_module_ids.size()); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.count(m1->module_id)); |
| + |
| + // Provide a redundant load message for that module. |
| + module_database_->OnModuleEvent( |
| + 1234, ModuleEvent(mojom::ModuleEventType::MODULE_LOADED, kDummyDll, |
| + 0xBAADF00D, 100 * 4096)); |
| + EXPECT_EQ(1u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + |
| + // Ensure that the process and module sets haven't changed. |
| + EXPECT_EQ(base::UTF8ToWide(kDummyDll), m1->module_path.value()); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m1->process_types); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->process_type); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p1->unloaded_module_ids.size()); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.count(m1->module_id)); |
| + |
| + // Load a second module into the process. |
| + module_database_->OnModuleEvent( |
| + 1234, ModuleEvent(mojom::ModuleEventType::MODULE_LOADED, kFooDll, |
| + 0xDEADBEEF, 20 * 4096)); |
| + EXPECT_EQ(2u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + |
| + // Ensure that the process and module sets are up to date. |
| + auto m2 = modules().rbegin(); |
| + EXPECT_EQ(base::UTF8ToWide(kFooDll), m2->module_path.value()); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m2->process_types); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->process_type); |
| + EXPECT_EQ(2u, p1->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p1->unloaded_module_ids.size()); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.count(m1->module_id)); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.count(m2->module_id)); |
| + |
| + // Unload the second module. |
| + module_database_->OnModuleEvent( |
| + 1234, ModuleEvent(mojom::ModuleEventType::MODULE_UNLOADED, kFooDll, |
| + 0xDEADBEEF, 20 * 4096)); |
| + EXPECT_EQ(2u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + |
| + // Ensure that the process and module sets are up to date. |
| + EXPECT_EQ(base::UTF8ToWide(kFooDll), m2->module_path.value()); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m2->process_types); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->process_type); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.size()); |
| + EXPECT_EQ(1u, p1->unloaded_module_ids.size()); |
| + EXPECT_EQ(1u, p1->loaded_module_ids.count(m1->module_id)); |
| + EXPECT_EQ(1u, p1->unloaded_module_ids.count(m2->module_id)); |
| + |
| + // Start a process. |
| + module_database_->OnProcessStarted(2345, content::PROCESS_TYPE_RENDERER); |
| + EXPECT_EQ(2u, modules().size()); |
| + EXPECT_EQ(2u, processes().size()); |
| + auto p2 = processes().rbegin(); |
| + EXPECT_EQ(2345, p2->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_RENDERER, p2->process_type); |
| + EXPECT_EQ(0u, p2->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p2->unloaded_module_ids.size()); |
| + |
| + // Load the dummy.dll in the second process as well. |
| + module_database_->OnModuleEvent( |
| + 2345, ModuleEvent(mojom::ModuleEventType::MODULE_ALREADY_LOADED, |
| + kDummyDll, 0xBAADF00D, 100 * 4096)); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) | |
| + ProcessTypeToBit(content::PROCESS_TYPE_RENDERER), |
| + m1->process_types); |
| + EXPECT_EQ(2345, p2->process_id); |
| + EXPECT_EQ(content::PROCESS_TYPE_RENDERER, p2->process_type); |
| + EXPECT_EQ(1u, p2->loaded_module_ids.size()); |
| + EXPECT_EQ(0u, p2->unloaded_module_ids.size()); |
| + EXPECT_EQ(1u, p2->loaded_module_ids.count(m1->module_id)); |
| + |
| + // End the second process without an explicit unload. This invalidates |p2|. |
| + module_database_->OnProcessEnded(2345); |
| + EXPECT_EQ(2u, modules().size()); |
| + EXPECT_EQ(1u, processes().size()); |
| + EXPECT_EQ(1234, p1->process_id); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) | |
| + ProcessTypeToBit(content::PROCESS_TYPE_RENDERER), |
| + m1->process_types); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m2->process_types); |
| + |
| + // End the first process without an explicit unload. This invalidates |p1|. |
| + module_database_->OnProcessEnded(1234); |
| + EXPECT_EQ(2u, modules().size()); |
| + EXPECT_EQ(0u, processes().size()); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) | |
| + ProcessTypeToBit(content::PROCESS_TYPE_RENDERER), |
| + m1->process_types); |
| + EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER), m2->process_types); |
| +} |