chromium/chrome/browser/ash/policy/remote_commands/device_command_get_available_routines_job_unittest.cc

// Copyright 2020 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/remote_commands/device_command_get_available_routines_job.h"

#include <memory>
#include <vector>

#include "base/json/json_writer.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace em = enterprise_management;

namespace {

// String constant identifying the routines field in the result payload.
const char* const kRoutinesFieldName = "routines";

constexpr RemoteCommandJob::UniqueIDType kUniqueID = 987123;

em::RemoteCommand GenerateCommandProto(RemoteCommandJob::UniqueIDType unique_id,
                                       base::TimeDelta age_of_command,
                                       base::TimeDelta idleness_cutoff,
                                       bool terminate_upon_input) {
  em::RemoteCommand command_proto;
  command_proto.set_type(
      em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES);
  command_proto.set_command_id(unique_id);
  command_proto.set_age_of_command(age_of_command.InMilliseconds());
  return command_proto;
}

}  // namespace

class DeviceCommandGetAvailableRoutinesJobTest : public testing::Test {
 protected:
  DeviceCommandGetAvailableRoutinesJobTest();
  DeviceCommandGetAvailableRoutinesJobTest(
      const DeviceCommandGetAvailableRoutinesJobTest&) = delete;
  DeviceCommandGetAvailableRoutinesJobTest& operator=(
      const DeviceCommandGetAvailableRoutinesJobTest&) = delete;
  ~DeviceCommandGetAvailableRoutinesJobTest() override;

  void InitializeJob(RemoteCommandJob* job,
                     RemoteCommandJob::UniqueIDType unique_id,
                     base::TimeTicks issued_time,
                     base::TimeDelta idleness_cutoff,
                     bool terminate_upon_input);

  std::string CreateSuccessPayload(
      const std::vector<ash::cros_healthd::mojom::DiagnosticRoutineEnum>&
          available_routines);

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  ::ash::mojo_service_manager::FakeMojoServiceManager fake_service_manager_;

  base::TimeTicks test_start_time_;
};

DeviceCommandGetAvailableRoutinesJobTest::
    DeviceCommandGetAvailableRoutinesJobTest() {
  ash::cros_healthd::FakeCrosHealthd::Initialize();
  test_start_time_ = base::TimeTicks::Now();
}

DeviceCommandGetAvailableRoutinesJobTest::
    ~DeviceCommandGetAvailableRoutinesJobTest() {
  ash::cros_healthd::FakeCrosHealthd::Shutdown();
}

void DeviceCommandGetAvailableRoutinesJobTest::InitializeJob(
    RemoteCommandJob* job,
    RemoteCommandJob::UniqueIDType unique_id,
    base::TimeTicks issued_time,
    base::TimeDelta idleness_cutoff,
    bool terminate_upon_input) {
  EXPECT_TRUE(job->Init(
      base::TimeTicks::Now(),
      GenerateCommandProto(unique_id, base::TimeTicks::Now() - issued_time,
                           idleness_cutoff, terminate_upon_input),
      em::SignedData()));

  EXPECT_EQ(unique_id, job->unique_id());
  EXPECT_EQ(RemoteCommandJob::NOT_STARTED, job->status());
}

std::string DeviceCommandGetAvailableRoutinesJobTest::CreateSuccessPayload(
    const std::vector<ash::cros_healthd::mojom::DiagnosticRoutineEnum>&
        available_routines) {
  std::string payload;

  base::Value::List routine_list;
  for (const auto& routine : available_routines) {
    routine_list.Append(static_cast<int>(routine));
  }
  auto root_dict =
      base::Value::Dict().Set(kRoutinesFieldName, std::move(routine_list));
  base::JSONWriter::Write(root_dict, &payload);
  return payload;
}

TEST_F(DeviceCommandGetAvailableRoutinesJobTest, Success) {
  const std::vector<ash::cros_healthd::mojom::DiagnosticRoutineEnum>
      kAvailableRoutines = {
          ash::cros_healthd::mojom::DiagnosticRoutineEnum::kUrandom,
          ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCapacity};
  ash::cros_healthd::FakeCrosHealthd::Get()->SetAvailableRoutinesForTesting(
      kAvailableRoutines);
  std::unique_ptr<RemoteCommandJob> job =
      std::make_unique<DeviceCommandGetAvailableRoutinesJob>();
  InitializeJob(job.get(), kUniqueID, test_start_time_, base::Seconds(30),
                /*terminate_upon_input=*/false);
  base::test::TestFuture<void> job_finished_future;
  bool success = job->Run(base::Time::Now(), base::TimeTicks::Now(),
                          job_finished_future.GetCallback());
  EXPECT_TRUE(success);
  ASSERT_TRUE(job_finished_future.Wait()) << "Job did not finish.";
  EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
  std::unique_ptr<std::string> payload = job->GetResultPayload();
  EXPECT_TRUE(payload);
  EXPECT_EQ(CreateSuccessPayload(kAvailableRoutines), *payload);
}

TEST_F(DeviceCommandGetAvailableRoutinesJobTest, Failure) {
  ash::cros_healthd::FakeCrosHealthd::Get()->SetAvailableRoutinesForTesting({});
  std::unique_ptr<RemoteCommandJob> job =
      std::make_unique<DeviceCommandGetAvailableRoutinesJob>();
  InitializeJob(job.get(), kUniqueID, test_start_time_, base::Seconds(30),
                /*terminate_upon_input=*/false);
  base::test::TestFuture<void> job_finished_future;
  bool success = job->Run(base::Time::Now(), base::TimeTicks::Now(),
                          job_finished_future.GetCallback());
  EXPECT_TRUE(success);
  ASSERT_TRUE(job_finished_future.Wait()) << "Job did not finish.";
  EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
  std::unique_ptr<std::string> payload = job->GetResultPayload();
  EXPECT_FALSE(payload);
}

}  // namespace policy