chromium/chrome/browser/ash/policy/reporting/os_updates/os_updates_reporter_unittest.cc

// Copyright 2023 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/ash/policy/reporting/os_updates/os_updates_reporter.h"

#include <memory>
#include <string_view>

#include "base/functional/callback_helpers.h"
#include "base/test/scoped_chromeos_version_info.h"
#include "chrome/browser/ash/policy/reporting/user_event_reporter_helper_testing.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/policy/messaging_layer/proto/synced/os_events.pb.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/reporting/client/mock_report_queue.h"
#include "components/reporting/proto/synced/record_constants.pb.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::Eq;
using testing::StrEq;

constexpr char kNewVersion[] = "1233.0.0";
constexpr char kLsbRelease[] =
    R"(CHROMEOS_RELEASE_NAME=Chrome OS
CHROMEOS_RELEASE_VERSION=11012.0.2018_08_28_1422
)";

namespace ash::reporting {

struct OsUpdatesReporterTestCase {
  update_engine::Operation operation;
  bool enterprise_rollback;
};

class TestHelper {
 public:
  TestHelper() = default;

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

  ~TestHelper() = default;

  void Init() {
    chromeos::PowerManagerClient::InitializeFake();
    SessionManagerClient::InitializeFake();
  }

  void Shutdown() {
    SessionManagerClient::Shutdown();
    chromeos::PowerManagerClient::Shutdown();
  }

  std::unique_ptr<::reporting::UserEventReporterHelperTesting>
  GetReporterHelper(bool reporting_enabled,
                    ::reporting::Status status = ::reporting::Status()) {
    record_.Clear();
    report_count_ = 0;
    auto mock_queue = std::unique_ptr<::reporting::MockReportQueue,
                                      base::OnTaskRunnerDeleter>(
        new ::reporting::MockReportQueue(),
        base::OnTaskRunnerDeleter(
            base::SequencedTaskRunner::GetCurrentDefault()));

    ON_CALL(*mock_queue, AddRecord(_, ::reporting::Priority::SECURITY, _))
        .WillByDefault(
            [this, status](std::string_view record_string,
                           ::reporting::Priority event_priority,
                           ::reporting::ReportQueue::EnqueueCallback cb) {
              ++report_count_;
              EXPECT_TRUE(record_.ParseFromArray(record_string.data(),
                                                 record_string.size()));
              std::move(cb).Run(status);
            });

    auto reporter_helper =
        std::make_unique<::reporting::UserEventReporterHelperTesting>(
            reporting_enabled, /*should_report_user=*/true,
            /*is_kiosk_user=*/false, std::move(mock_queue));
    return reporter_helper;
  }

  OsEventsRecord GetRecord() { return record_; }

  int GetReportCount() { return report_count_; }

 private:
  content::BrowserTaskEnvironment task_environment_;

  OsEventsRecord record_;
  int report_count_ = 0;
};

class OsUpdatesReporterTest
    : public ::testing::TestWithParam<OsUpdatesReporterTestCase> {
 protected:
  OsUpdatesReporterTest() {}

  void SetUp() override { test_helper_.Init(); }

  void TearDown() override { test_helper_.Shutdown(); }

  TestHelper test_helper_;
};

TEST_F(OsUpdatesReporterTest, ReportSuccessfulUpdatePolicyEnabled) {
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake update client.
  ash::FakeUpdateEngineClient* fake_update_engine_client_ =
      ash::UpdateEngineClient::InitializeFakeForTest();

  // Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/true);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Build and send update status.
  update_engine::StatusResult status;
  status.set_new_version(kNewVersion);
  status.set_is_enterprise_rollback(false);
  status.set_current_operation(update_engine::Operation::UPDATED_NEED_REBOOT);
  fake_update_engine_client_->NotifyObserversThatStatusChanged(status);

  // Verify event.
  const OsEventsRecord& record = test_helper_.GetRecord();
  ASSERT_EQ(test_helper_.GetReportCount(), 1);
  ASSERT_TRUE(record.has_update_event());
  EXPECT_TRUE(record.has_event_timestamp_sec());
  EXPECT_EQ(record.target_os_version(), kNewVersion);
  EXPECT_EQ(record.os_operation_type(), reporting::OsOperationType::SUCCESS);
}

TEST_F(OsUpdatesReporterTest, ReportSuccessfulUpdatePolicyDisabled) {
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake update client.
  ash::FakeUpdateEngineClient* fake_update_engine_client_ =
      ash::UpdateEngineClient::InitializeFakeForTest();

  // Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/false);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Build and send update status.
  update_engine::StatusResult status;
  status.set_new_version(kNewVersion);
  status.set_is_enterprise_rollback(false);
  status.set_current_operation(update_engine::Operation::UPDATED_NEED_REBOOT);
  fake_update_engine_client_->NotifyObserversThatStatusChanged(status);

  // Verify event.
  ASSERT_EQ(test_helper_.GetReportCount(), 0);
}

TEST_P(OsUpdatesReporterTest, ReportFailedUpdateRollbackPolicyEnabled) {
  const auto test_case = GetParam();
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake update client.
  ash::FakeUpdateEngineClient* fake_update_engine_client_ =
      ash::UpdateEngineClient::InitializeFakeForTest();

  // Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/true);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Build and send update status.
  update_engine::StatusResult status;
  status.set_new_version(kNewVersion);
  status.set_is_enterprise_rollback(test_case.enterprise_rollback);
  status.set_current_operation(test_case.operation);
  fake_update_engine_client_->NotifyObserversThatStatusChanged(status);

  // Verify event.
  const OsEventsRecord& record = test_helper_.GetRecord();
  ASSERT_EQ(test_helper_.GetReportCount(), 1);
  if (test_case.enterprise_rollback) {
    ASSERT_TRUE(record.has_rollback_event());
  } else {
    ASSERT_TRUE(record.has_update_event());
  }
  EXPECT_TRUE(record.has_event_timestamp_sec());
  EXPECT_EQ(record.target_os_version(), kNewVersion);
  EXPECT_EQ(record.os_operation_type(), reporting::OsOperationType::FAILURE);
}

TEST_P(OsUpdatesReporterTest, ReportFailedUpdateRollbackPolicyDisabled) {
  const auto test_case = GetParam();
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake update client.
  ash::FakeUpdateEngineClient* fake_update_engine_client_ =
      ash::UpdateEngineClient::InitializeFakeForTest();

  // Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/false);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Build and send update status.
  update_engine::StatusResult status;
  status.set_new_version(kNewVersion);
  status.set_is_enterprise_rollback(test_case.enterprise_rollback);
  status.set_current_operation(test_case.operation);
  fake_update_engine_client_->NotifyObserversThatStatusChanged(status);

  // Verify event.
  ASSERT_EQ(test_helper_.GetReportCount(), 0);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    OsUpdatesReporterTest,
    ::testing::ValuesIn<OsUpdatesReporterTestCase>(
        {{/*operation=*/update_engine::Operation::ERROR,
          /*enterprise_rollback=*/true},
         {/*operation=*/update_engine::Operation::ERROR,
          /*enterprise_rollback=*/false},
         {/*operation=*/update_engine::Operation::REPORTING_ERROR_EVENT,
          /*enterprise_rollback=*/true},
         {/*operation=*/update_engine::Operation::REPORTING_ERROR_EVENT,
          /*enterprise_rollback=*/false}}));

class PowerwashTest : public ::testing::TestWithParam<bool> {
 protected:
  PowerwashTest() {}

  void SetUp() override {
    test_helper_.Init();
    ash::UpdateEngineClient::InitializeFakeForTest();
    remote_requested_ = GetParam();
  }

  void TearDown() override {
    test_helper_.Shutdown();
    ash::UpdateEngineClient::Shutdown();
  }

  TestHelper test_helper_;
  bool remote_requested_;
};

TEST_P(PowerwashTest, PolicyEnabled) {
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake session manager.
  ash::SessionManagerClient::InitializeFake();
  FakeSessionManagerClient* session_manager = FakeSessionManagerClient::Get();

  //  Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/true);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Fake a powerwash.
  if (remote_requested_) {
    session_manager->StartRemoteDeviceWipe(enterprise_management::SignedData());
  } else {
    session_manager->StartDeviceWipe(base::DoNothing());
  }

  // Verify event.
  const OsEventsRecord& record = test_helper_.GetRecord();
  ASSERT_EQ(test_helper_.GetReportCount(), 1);
  ASSERT_EQ(session_manager->start_device_wipe_call_count(), 1);
  ASSERT_TRUE(record.has_powerwash_event());
  ASSERT_EQ(record.os_operation_type(), reporting::OsOperationType::INITIATED);
  EXPECT_EQ(record.powerwash_event().remote_request(), remote_requested_);
  EXPECT_TRUE(record.has_event_timestamp_sec());
}

TEST_P(PowerwashTest, PolicyDisabled) {
  // Set current OS Version.
  base::test::ScopedChromeOSVersionInfo scoped_version(
      kLsbRelease, /*lsb_release_time=*/base::Time());

  // Set up fake session manager.
  ash::SessionManagerClient::InitializeFake();
  FakeSessionManagerClient* session_manager = FakeSessionManagerClient::Get();

  //  Set up reporter.
  auto reporter_helper = test_helper_.GetReporterHelper(
      /*reporting_enabled=*/false);
  auto reporter = ::reporting::OsUpdatesReporter::CreateForTesting(
      std::move(reporter_helper));

  // Fake a powerwash.
  if (remote_requested_) {
    session_manager->StartRemoteDeviceWipe(enterprise_management::SignedData());
  } else {
    session_manager->StartDeviceWipe(base::DoNothing());
  }

  // Verify that no event was reported.
  ASSERT_EQ(test_helper_.GetReportCount(), 0);
  // Verify that the powerwash is still going to happen.
  ASSERT_EQ(session_manager->start_device_wipe_call_count(), 1);
}

INSTANTIATE_TEST_SUITE_P(All, PowerwashTest, ::testing::Bool());

}  // namespace ash::reporting