chromium/chrome/browser/win/conflicts/third_party_conflicts_manager_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/win/conflicts/third_party_conflicts_manager.h"

#include <optional>
#include <utility>

#include "base/base_paths.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/win/conflicts/module_info.h"
#include "chrome/browser/win/conflicts/proto/module_list.pb.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

class ThirdPartyConflictsManagerTest : public testing::Test,
                                       public ModuleDatabaseEventSource {
 public:
  ThirdPartyConflictsManagerTest()
      : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {}

  ThirdPartyConflictsManagerTest(const ThirdPartyConflictsManagerTest&) =
      delete;
  ThirdPartyConflictsManagerTest& operator=(
      const ThirdPartyConflictsManagerTest&) = delete;

  void SetUp() override {
    ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());

    scoped_feature_list_.InitWithFeatures(
        // Enabled features.
        {features::kIncompatibleApplicationsWarning,
         features::kThirdPartyModulesBlocking},
        // Disabled features.
        {});
  }

  // Returns the path to the module list.
  base::FilePath GetModuleListPath() const {
    return scoped_temp_dir_.GetPath().Append(L"ModuleList.bin");
  }

  // Writes an empty serialized ModuleList proto to |GetModuleListPath()|.
  void CreateModuleList() {
    chrome::conflicts::ModuleList module_list;
    // Include an empty blocklist and allowlist.
    module_list.mutable_blocklist();
    module_list.mutable_allowlist();

    std::string contents;
    ASSERT_TRUE(module_list.SerializeToString(&contents));
    ASSERT_TRUE(base::WriteFile(GetModuleListPath(), contents));
  }

  void OnManagerInitializationComplete(
      base::OnceClosure quit_closure,
      ThirdPartyConflictsManager::State final_state) {
    final_state_ = final_state;
    std::move(quit_closure).Run();
  }

  const std::optional<ThirdPartyConflictsManager::State>& final_state() {
    return final_state_;
  }

  // ModuleDatabaseEventSource:
  void AddObserver(ModuleDatabaseObserver* observer) override {}
  void RemoveObserver(ModuleDatabaseObserver* observer) override {}

 private:
  content::BrowserTaskEnvironment task_environment_;
  ScopedTestingLocalState scoped_testing_local_state_;

  // Temp directory used to host module list.
  base::ScopedTempDir scoped_temp_dir_;

  base::test::ScopedFeatureList scoped_feature_list_;

  std::optional<ThirdPartyConflictsManager::State> final_state_;
};

std::pair<ModuleInfoKey, ModuleInfoData> CreateExeModuleInfo() {
  base::FilePath exe_path;
  base::PathService::Get(base::FILE_EXE, &exe_path);

  std::pair<ModuleInfoKey, ModuleInfoData> module_info(
      std::piecewise_construct,
      std::forward_as_tuple(std::move(exe_path), 0, 0),
      std::forward_as_tuple());

  module_info.second.inspection_result =
      std::make_optional<ModuleInspectionResult>();

  return module_info;
}

TEST_F(ThirdPartyConflictsManagerTest, InitializeUpdaters) {
  ThirdPartyConflictsManager third_party_conflicts_manager(this);

  // The ThirdPartyConflictsManager class looks for the certificate info of the
  // current exe via the ModuleDatabaseObserver interface.
  auto exe_module_info = CreateExeModuleInfo();
  third_party_conflicts_manager.OnNewModuleFound(exe_module_info.first,
                                                 exe_module_info.second);

  third_party_conflicts_manager.OnModuleDatabaseIdle();
  ASSERT_NO_FATAL_FAILURE(CreateModuleList());
  third_party_conflicts_manager.LoadModuleList(GetModuleListPath());

  base::RunLoop run_loop;
  third_party_conflicts_manager.ForceInitialization(base::BindRepeating(
      &ThirdPartyConflictsManagerTest::OnManagerInitializationComplete,
      base::Unretained(this), run_loop.QuitClosure()));

  run_loop.Run();

  ASSERT_TRUE(final_state().has_value());

  EXPECT_EQ(final_state().value(),
            ThirdPartyConflictsManager::State::kWarningAndBlockingInitialized);
}

TEST_F(ThirdPartyConflictsManagerTest, InvalidModuleList) {
  ThirdPartyConflictsManager third_party_conflicts_manager(this);

  third_party_conflicts_manager.OnModuleDatabaseIdle();

  // Pass in an empty path which will ensure that the deserialization will fail.
  third_party_conflicts_manager.LoadModuleList(GetModuleListPath());

  base::RunLoop run_loop;
  third_party_conflicts_manager.ForceInitialization(base::BindRepeating(
      &ThirdPartyConflictsManagerTest::OnManagerInitializationComplete,
      base::Unretained(this), run_loop.QuitClosure()));

  run_loop.Run();

  ASSERT_TRUE(final_state().has_value());
  EXPECT_EQ(final_state().value(),
            ThirdPartyConflictsManager::State::kModuleListInvalidFailure);
}

TEST_F(ThirdPartyConflictsManagerTest, DestroyManager) {
  auto third_party_conflicts_manager =
      std::make_unique<ThirdPartyConflictsManager>(this);

  third_party_conflicts_manager->OnModuleDatabaseIdle();
  ASSERT_NO_FATAL_FAILURE(CreateModuleList());
  third_party_conflicts_manager->LoadModuleList(GetModuleListPath());

  base::RunLoop run_loop;
  third_party_conflicts_manager->ForceInitialization(base::BindRepeating(
      &ThirdPartyConflictsManagerTest::OnManagerInitializationComplete,
      base::Unretained(this), run_loop.QuitClosure()));

  // Delete the instance while it is initializing.
  third_party_conflicts_manager = nullptr;
  run_loop.Run();

  ASSERT_TRUE(final_state().has_value());
  EXPECT_EQ(final_state().value(),
            ThirdPartyConflictsManager::State::kDestroyed);
}