chromium/chrome/browser/component_updater/recovery_improved_component_unittest.cc

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

#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
#include <utility>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/recovery_improved_component_installer.h"
#include "components/crx_file/crx_verifier.h"
#include "components/services/unzip/content/unzip_service.h"
#include "components/services/unzip/in_process_unzipper.h"
#include "components/update_client/test_utils.h"
#include "components/update_client/update_client_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

namespace component_updater {

namespace {

// The public key hash for the test CRX.
constexpr uint8_t kKeyHashTest[] = {
    0x69, 0xfc, 0x41, 0xf6, 0x17, 0x20, 0xc6, 0x36, 0x92, 0xcd, 0x95,
    0x76, 0x69, 0xf6, 0x28, 0xcc, 0xbe, 0x98, 0x4b, 0x93, 0x17, 0xd6,
    0x9c, 0xb3, 0x64, 0x0c, 0x0d, 0x25, 0x61, 0xc5, 0x80, 0x1d};

class RecoveryImprovedActionHandlerTest : public PlatformTest {
 public:
  RecoveryImprovedActionHandlerTest() = default;

 protected:
  base::ScopedTempDir temp_dir_;

 private:
  base::test::TaskEnvironment task_environment_;
};

class TestActionHandler : public RecoveryComponentActionHandler {
 public:
  // Accepts a test CRX without a production publisher proof.
  TestActionHandler()
      : RecoveryComponentActionHandler(
            {std::begin(kKeyHashTest), std::end(kKeyHashTest)},
            crx_file::VerifierFormat::CRX3) {}
  TestActionHandler(const TestActionHandler&) = delete;
  TestActionHandler& operator=(const TestActionHandler&) = delete;

 protected:
  ~TestActionHandler() override = default;

 private:
  // Overrides for RecoveryComponentActionHandler.
  base::CommandLine MakeCommandLine(
      const base::FilePath& unpack_path) const override;
  void PrepareFiles(const base::FilePath& unpack_path) const override;
  void Elevate(Callback callback) override;
};

base::CommandLine TestActionHandler::MakeCommandLine(
    const base::FilePath& unpack_path) const {
  base::CommandLine command_line(
      unpack_path.Append(FILE_PATH_LITERAL("ChromeRecovery.exe")));
  return command_line;
}

void TestActionHandler::PrepareFiles(const base::FilePath& unpack_path) const {}

// This test fixture only tests the per-user execution flow.
void TestActionHandler::Elevate(Callback callback) {
  NOTREACHED_IN_MIGRATION();
}

}  // namespace

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
TEST_F(RecoveryImprovedActionHandlerTest, HandleError) {
  unzip::SetUnzipperLaunchOverrideForTesting(
      base::BindRepeating(&unzip::LaunchInProcessUnzipper));

  // Tests the error is propagated through the callback in the error case.
  base::RunLoop runloop;
  base::MakeRefCounted<TestActionHandler>()->Handle(
      base::FilePath{FILE_PATH_LITERAL("not-found")}, "some-session-id",
      base::BindOnce(
          [](base::OnceClosure quit_closure, bool succeeded, int error_code,
             int extra_code1) {
            EXPECT_FALSE(succeeded);
            EXPECT_EQ(update_client::UnpackerError::kInvalidFile,
                      static_cast<update_client::UnpackerError>(error_code));
            EXPECT_EQ(2, extra_code1);
            std::move(quit_closure).Run();
          },
          runloop.QuitClosure()));
  runloop.Run();
}
#endif  //  BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)

#if BUILDFLAG(IS_WIN)
TEST_F(RecoveryImprovedActionHandlerTest, HandleSuccess) {
  unzip::SetUnzipperLaunchOverrideForTesting(
      base::BindRepeating(&unzip::LaunchInProcessUnzipper));

  // Tests that the recovery program runs and it returns an expected value.
  constexpr char kActionRunFileName[] = "ChromeRecovery.crx3";
  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  const base::FilePath from_path =
      update_client::GetTestFilePath(kActionRunFileName);
  const base::FilePath to_path =
      temp_dir_.GetPath().AppendASCII(kActionRunFileName);
  ASSERT_TRUE(base::CopyFile(from_path, to_path));

  base::RunLoop runloop;
  base::MakeRefCounted<TestActionHandler>()->Handle(
      to_path, "some-session-id",
      base::BindOnce(
          [](base::OnceClosure quit_closure, bool succeeded, int error_code,
             int extra_code1) {
            EXPECT_TRUE(succeeded);
            EXPECT_EQ(1877345072, error_code);
            EXPECT_EQ(0, extra_code1);
            std::move(quit_closure).Run();
          },
          runloop.QuitClosure()));
  {
    // For some reason, the task which runs the wait for the recovery EXE
    // execution is handled with some delay. This causes the run loop to
    // fail with a timeout.
    const base::test::ScopedRunLoopTimeout specific_timeout(FROM_HERE,
                                                            base::Seconds(60));
    runloop.Run();
  }
}
#endif  // BUILDFLAG(IS_WIN)

}  // namespace component_updater