chromium/chrome/installer/setup/update_active_setup_version_work_item_unittest.cc

// Copyright 2015 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/installer/setup/update_active_setup_version_work_item.h"

#include <windows.h>

#include <ostream>

#include "base/strings/utf_string_conversions.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::ValuesIn;

namespace {

const HKEY kActiveSetupRoot = HKEY_LOCAL_MACHINE;
const wchar_t kActiveSetupPath[] = L"Active Setup\\test";

struct UpdateActiveSetupVersionWorkItemTestCase {
  // The initial value to be set in the registry prior to executing the
  // UpdateActiveSetupVersionWorkItem. No value will be set if this null.
  const wchar_t* initial_value;

  // Whether to ask the UpdateActiveSetupVersionWorkItem to bump the
  // SELECTIVE_TRIGGER component of the Active Setup version.
  bool bump_selective_trigger;

  // The expected value after executing the UpdateActiveSetupVersionWorkItem.
  const wchar_t* expected_result;
} const kUpdateActiveSetupVersionWorkItemTestCases[] = {
    // Initial install.
    {nullptr, false, L"43,0,0,0"},
    // No-op update.
    {L"43,0,0,0", false, L"43,0,0,0"},
    // Update only major component.
    {L"24,1,2,3", false, L"43,0,0,0"},
    // Reset from bogus value.
    {L"zzz", false, L"43,0,0,0"},
    // Reset from invalid version (too few components).
    {L"1,2", false, L"43,0,0,0"},
    // Reset from invalid version (too many components).
    {L"43,1,2,3,10", false, L"43,1,2,3"},
    // Reset from empty string.
    {L"", false, L"43,0,0,0"},

    // Same tests with a SELECTIVE_TRIGGER component bump.
    {nullptr, true, L"43,0,0,0"},
    {L"43,0,0,0", true, L"43,0,1,0"},
    {L"43,0,46,0", true, L"43,0,47,0"},
    {L"24,1,2,3", true, L"43,0,0,0"},
    {L"zzz", true, L"43,0,0,0"},
    {L"1,2", true, L"43,0,0,0"},
    {L"43,1,2,3,10", true, L"43,1,3,3"},
    {L"", true, L"43,0,0,0"},
    // Bumping a negative selective trigger component should result in it being
    // reset and subsequently bumped to 1.
    {L"43,11,-123,33", true, L"43,11,1,33"},
};

// Implements PrintTo in order for gtest to be able to print the problematic
// UpdateActiveSetupVersionWorkItemTestCase on failure.
void PrintTo(const UpdateActiveSetupVersionWorkItemTestCase& test_case,
             ::std::ostream* os) {
  *os << "Initial value: "
      << (test_case.initial_value ? base::WideToUTF8(test_case.initial_value)
                                  : "(empty)")
      << ", bump_selective_trigger: " << test_case.bump_selective_trigger
      << ", expected result: " << base::WideToUTF8(test_case.expected_result);
}

}  // namespace

class UpdateActiveSetupVersionWorkItemTest
    : public testing::TestWithParam<UpdateActiveSetupVersionWorkItemTestCase> {
 public:
  UpdateActiveSetupVersionWorkItemTest() {}

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

  void SetUp() override {
    ASSERT_NO_FATAL_FAILURE(
        registry_override_manager_.OverrideRegistry(kActiveSetupRoot));
  }

 private:
  registry_util::RegistryOverrideManager registry_override_manager_;
};

TEST_P(UpdateActiveSetupVersionWorkItemTest, Execute) {
  // Get the parametrized |test_case| which defines 5 steps:
  //   1) Maybe set an initial Active Setup version in the registry according to
  //      |test_case.initial_value|.
  //   2) Declare the work to be done by the UpdateActiveSetupVersionWorkItem
  //      based on |test_case.bump_selective_trigger|.
  //   3) Unconditionally execute the Active Setup work items.
  //   4) Verify that the updated Active Setup version is as expected by
  //      |test_case.expected_result|.
  //   5) Rollback and verify that |test_case.initial_value| is back.
  const UpdateActiveSetupVersionWorkItemTestCase& test_case = GetParam();

  base::win::RegKey test_key;

  ASSERT_EQ(ERROR_FILE_NOT_FOUND,
            test_key.Open(kActiveSetupRoot, kActiveSetupPath, KEY_READ));

  UpdateActiveSetupVersionWorkItem active_setup_work_item(
      kActiveSetupPath,
      test_case.bump_selective_trigger
          ? UpdateActiveSetupVersionWorkItem::UPDATE_AND_BUMP_SELECTIVE_TRIGGER
          : UpdateActiveSetupVersionWorkItem::UPDATE);

  // Create the key and set the |initial_value| *after* the WorkItem to confirm
  // that all of the work is done when executing the item, not when creating it.
  ASSERT_EQ(ERROR_SUCCESS,
            test_key.Create(kActiveSetupRoot, kActiveSetupPath, KEY_SET_VALUE));
  if (test_case.initial_value) {
    ASSERT_EQ(ERROR_SUCCESS,
              test_key.WriteValue(L"Version", test_case.initial_value));
  }

  EXPECT_TRUE(active_setup_work_item.Do());

  {
    std::wstring version_out;
    EXPECT_EQ(ERROR_SUCCESS, test_key.Open(kActiveSetupRoot, kActiveSetupPath,
                                           KEY_QUERY_VALUE));
    EXPECT_EQ(ERROR_SUCCESS, test_key.ReadValue(L"Version", &version_out));
    EXPECT_EQ(test_case.expected_result, version_out);
  }

  active_setup_work_item.Rollback();

  {
    EXPECT_EQ(ERROR_SUCCESS, test_key.Open(kActiveSetupRoot, kActiveSetupPath,
                                           KEY_QUERY_VALUE));

    std::wstring version_out;
    LONG read_result = test_key.ReadValue(L"Version", &version_out);
    if (test_case.initial_value) {
      EXPECT_EQ(ERROR_SUCCESS, read_result);
      EXPECT_EQ(test_case.initial_value, version_out);
    } else {
      EXPECT_EQ(ERROR_FILE_NOT_FOUND, read_result);
    }
  }
}

INSTANTIATE_TEST_SUITE_P(UpdateActiveSetupVersionWorkItemTestInstance,
                         UpdateActiveSetupVersionWorkItemTest,
                         ValuesIn(kUpdateActiveSetupVersionWorkItemTestCases));