chromium/android_webview/nonembedded/component_updater/aw_component_installer_policy_unittest.cc

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

#include "android_webview/nonembedded/component_updater/aw_component_installer_policy.h"

#include <stdint.h>

#include <iterator>
#include <memory>
#include <utility>

#include "android_webview/common/aw_paths.h"
#include "base/android/path_utils.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/scoped_path_override.h"
#include "base/values.h"
#include "base/version.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace android_webview {

namespace {

constexpr char kComponentId[] = "jebgalgnebhfojomionfpkfelancnnkf";
// This hash corresponds to kComponentId.
constexpr uint8_t kSha256Hash[] = {
    0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, 0x8e, 0xd5, 0xfa,
    0x54, 0xb0, 0xd2, 0xdd, 0xa5, 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47,
    0xf6, 0xc4, 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};

base::Value::Dict test_manifest(const base::Version& version) {
  base::Value::Dict manifest;
  manifest.Set("version", version.GetString());
  return manifest;
}

void CreateTestFiles(const base::FilePath& install_dir) {
  base::CreateDirectory(install_dir);
  ASSERT_TRUE(base::WriteFile(install_dir.AppendASCII("file1.txt"), "1"));
  ASSERT_TRUE(base::WriteFile(install_dir.AppendASCII("file2.txt"), "2"));
  ASSERT_TRUE(base::CreateDirectory(install_dir.AppendASCII("sub_dir")));
  ASSERT_TRUE(base::WriteFile(
      install_dir.AppendASCII("sub_dir").AppendASCII("file3.txt"), "3"));
}

void AssertTestFiles(const base::FilePath& install_dir) {
  EXPECT_TRUE(base::PathExists(install_dir.AppendASCII("file1.txt")));
  EXPECT_TRUE(base::PathExists(install_dir.AppendASCII("file2.txt")));
  EXPECT_TRUE(base::DirectoryExists(install_dir.AppendASCII("sub_dir")));
  EXPECT_TRUE(base::PathExists(
      install_dir.AppendASCII("sub_dir").AppendASCII("file3.txt")));
}

}  // namespace

class TestAwComponentInstallerPolicy : public AwComponentInstallerPolicy {
 public:
  TestAwComponentInstallerPolicy() {
    ON_CALL(*this, GetHash).WillByDefault([](std::vector<uint8_t>* hash) {
      hash->assign(std::begin(kSha256Hash), std::end(kSha256Hash));
    });
  }
  ~TestAwComponentInstallerPolicy() override = default;

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

  MOCK_METHOD2(OnCustomInstall,
               update_client::CrxInstaller::Result(const base::Value::Dict&,
                                                   const base::FilePath&));
  MOCK_CONST_METHOD2(VerifyInstallation,
                     bool(const base::Value::Dict& manifest,
                          const base::FilePath& dir));
  MOCK_CONST_METHOD0(SupportsGroupPolicyEnabledComponentUpdates, bool());
  MOCK_CONST_METHOD0(RequiresNetworkEncryption, bool());
  MOCK_CONST_METHOD0(GetRelativeInstallDir, base::FilePath());
  MOCK_CONST_METHOD0(GetName, std::string());
  MOCK_CONST_METHOD0(GetInstallerAttributes,
                     update_client::InstallerAttributes());
  MOCK_CONST_METHOD1(GetHash, void(std::vector<uint8_t>*));

 private:
  void IncrementComponentsUpdatedCount() override { /* noop */
  }
};

class AwComponentInstallerPolicyTest : public testing::Test {
 public:
  AwComponentInstallerPolicyTest() = default;
  ~AwComponentInstallerPolicyTest() override = default;

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

  // Override from testing::Test
  void SetUp() override {
    scoped_path_override_ =
        std::make_unique<base::ScopedPathOverride>(DIR_COMPONENTS_TEMP);

    ASSERT_TRUE(base::android::GetDataDirectory(&cps_component_path_));
    cps_component_path_ = cps_component_path_.AppendASCII("components")
                              .AppendASCII("cps")
                              .AppendASCII(kComponentId);

    ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
    CreateTestFiles(GetTestInstallPath());

    delegate_ = std::make_unique<TestAwComponentInstallerPolicy>();
  }

  void TearDown() override {
    ASSERT_TRUE(base::DeletePathRecursively(cps_component_path_));
  }

  base::FilePath GetTestInstallPath() const {
    return scoped_temp_dir_.GetPath();
  }

 protected:
  base::FilePath cps_component_path_;
  std::unique_ptr<TestAwComponentInstallerPolicy> delegate_;

 private:
  base::ScopedTempDir scoped_temp_dir_;
  std::unique_ptr<base::ScopedPathOverride> scoped_path_override_;
};

TEST_F(AwComponentInstallerPolicyTest, TestNoExistingVersions) {
  const base::Version testVersion("1.2.3.4");

  delegate_->ComponentReady(testVersion, GetTestInstallPath(),
                            test_manifest(testVersion));

  // Check that the original install path still has files.
  AssertTestFiles(GetTestInstallPath());

  AssertTestFiles(
      cps_component_path_.AppendASCII("1_" + testVersion.GetString()));
}

TEST_F(AwComponentInstallerPolicyTest, TestExistingOtherVersions) {
  const base::Version testVersion("1.2.3.4");

  CreateTestFiles(cps_component_path_.AppendASCII("1_4.3.2.1"));
  CreateTestFiles(cps_component_path_.AppendASCII("10_2.3.4.1"));

  delegate_->ComponentReady(testVersion, GetTestInstallPath(),
                            test_manifest(testVersion));

  // Check that the original install path still has files.
  AssertTestFiles(GetTestInstallPath());

  AssertTestFiles(
      cps_component_path_.AppendASCII("11_" + testVersion.GetString()));
}

TEST_F(AwComponentInstallerPolicyTest, TestExistingSameVersion) {
  const base::Version testVersion("1.2.3.4");

  CreateTestFiles(
      cps_component_path_.AppendASCII("5_" + testVersion.GetString()));

  delegate_->ComponentReady(testVersion, GetTestInstallPath(),
                            test_manifest(testVersion));

  // Check that the original install path still has files.
  AssertTestFiles(GetTestInstallPath());

  // Directory should only contain "<component-id>/5/1.2.3.4/", no other files
  // or directories should exist.
  base::FileEnumerator file_enumerator(
      cps_component_path_, /* recursive= */ false,
      base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    EXPECT_EQ(path.BaseName().MaybeAsASCII(), "5_" + testVersion.GetString());
    AssertTestFiles(path);
  }
}

}  // namespace android_webview