chromium/chromeos/ash/components/fwupd/firmware_update_manager_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 "chromeos/ash/components/fwupd/firmware_update_manager.h"

#include <cstdint>
#include <deque>
#include <map>
#include <memory>
#include <string>

#include "ash/system/firmware_update/firmware_update_notification_controller.h"
#include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "chromeos/ash/components/dbus/fwupd/dbus_constants.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_client.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_request.h"
#include "chromeos/ash/components/fwupd/fake_fwupd_download_client.h"
#include "chromeos/ash/components/fwupd/histogram_util.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/message_center/fake_message_center.h"
#include "ui/message_center/message_center.h"

namespace {

using ::testing::_;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;

const char kFakeDeviceIdForTesting[] = "Fake_Device_ID";
const char kFakeInternalDeviceIdForTesting[] = "Fake_Internal_Device_ID";
const char kFakeDeviceNameForTesting[] = "Fake Device Name";
const char kFakeInternalDeviceNameForTesting[] = "Fake Internal Device Name";
const char kFakeUpdateDescriptionForTesting[] =
    "This is a fake update for testing.";
const uint32_t kFakeUpdatePriorityForTesting = 1;
const uint32_t kFakeCriticalUpdatePriorityForTesting = 3;
const char kFakeUpdateVersionForTesting[] = "1.0.0";
const char kFakeUpdateUriForTesting[] =
    "file:///usr/share/fwupd/remotes.d/vendor/firmware/testFirmwarePath-V1.cab";
const char kFakeUpdateFileNameForTesting[] = "testFirmwarePath-V1.cab";
const char kChecksumFileUriForTesting[] =
    "https://storage.googleapis.com/chromeos-localmirror/lvfs/"
    "firmware.xml.xz.jcat";
const char kFirmwareFileUriForTesting[] =
    "https://storage.googleapis.com/chromeos-localmirror/lvfs/"
    "firmware-07171-test.xml.xz";
const char kEmptyFileSha256ForTesting[] =
    "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
const char kFilePathIdentifier[] = "file://";
const char kDescriptionKey[] = "Description";
const char kIdKey[] = "DeviceId";
const char kNameKey[] = "Name";
const char kPriorityKey[] = "Urgency";
const char kUriKey[] = "Uri";
const char kVersionKey[] = "Version";
const char kChecksumKey[] = "Checksum";
const char kDownloadDir[] = "firmware-updates";
const char kCacheDir[] = "cache";
const char kCabExtension[] = ".cab";
const char kFirmwareUpdateNotificationId[] =
    "cros_firmware_update_notification_id";
const char kFlagsKey[] = "Flags";
const uint64_t kFakeFlagForTesting = 1;
const char kTrustFlagsKey[] = "TrustFlags";
const uint64_t kFakeReportFlagForTesting = 1llu << 8;

/*
  Test checksum json file mimicking a real checksum data file
  {"JcatVersionMajor": 0, "JcatVersionMinor": 1, "Items": [{"Id":
  "firmware-07171-test.xml.xz", "AliasIds": ["firmware.xml.xz"], "Blobs":
  [{"Kind": 4, "Flags": 1, "Timestamp": 0, "Data": "test-data"}, {"Kind": 1,
  "Flags": 1, "Timestamp": 0, "Data": "test-data"}, {"Kind": 3, "Flags": 1,
  "Timestamp": 1713183334, "Data": "test-data"}]}]}
*/
// This is the string representation of gzip compressed string above which
// is the real value from a checksum file. It was obtained by running echo
// -n $STRING | gzip -c | hexdump -e '8 1 ", 0x%x"'
const uint8_t kTestChecksumData[] = {
    0x1f, 0x8b, 0x8,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x3,  0xab, 0x56,
    0xf2, 0x4a, 0x4e, 0x2c, 0x9,  0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3,
    0x4d, 0xcc, 0xca, 0x2f, 0x52, 0xb2, 0x52, 0x30, 0xd0, 0x51, 0x40, 0x11,
    0xce, 0xcc, 0x3,  0xb,  0x1b, 0x2,  0x85, 0x3d, 0x4b, 0x52, 0x73, 0x8b,
    0x81, 0xec, 0xe8, 0x6a, 0x25, 0xcf, 0x14, 0x20, 0xad, 0x94, 0x96, 0x59,
    0x94, 0x5b, 0x9e, 0x58, 0x94, 0xaa, 0x6b, 0x60, 0x6e, 0x68, 0x6e, 0xa8,
    0x5b, 0x92, 0x5a, 0x5c, 0xa2, 0x57, 0x91, 0x9b, 0xa3, 0x57, 0x51, 0xa5,
    0x4,  0x54, 0xee, 0x98, 0x93, 0x99, 0x58, 0xec, 0x99, 0x2,  0xd6, 0x1,
    0x57, 0xa,  0x93, 0x8f, 0x5,  0x2a, 0x70, 0xca, 0xc9, 0x4f, 0x82, 0x9a,
    0xe7, 0x9d, 0x99, 0x7,  0x32, 0xd1, 0x4,  0x28, 0xea, 0x96, 0x93, 0x98,
    0x5e, 0xc,  0xb5, 0x31, 0x24, 0x33, 0x17, 0x68, 0x66, 0x62, 0x6e, 0x1,
    0xd4, 0x61, 0x2e, 0x89, 0x25, 0x89, 0x20, 0x8b, 0x41, 0x36, 0xe9, 0xa6,
    0x80, 0x38, 0xb5, 0x3a, 0xa,  0x70, 0xdd, 0x86, 0x14, 0xe9, 0x36, 0xc6,
    0xa3, 0x1b, 0xe8, 0x3b, 0x63, 0x43, 0xb,  0x63, 0x63, 0x63, 0x13, 0xec,
    0xc6, 0xc4, 0x2,  0x21, 0x0,  0x64, 0x30, 0x78, 0x6e, 0x4e, 0x1,  0x0,
    0x0};

// "Id" key changed to "ID" in the above json string
const uint8_t kIncorrectTestChecksumData[] = {
    0x1f, 0x8b, 0x8,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x3,  0xab, 0x56,
    0xf2, 0x4a, 0x4e, 0x2c, 0x9,  0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3,
    0x4d, 0xcc, 0xca, 0x2f, 0x52, 0xb2, 0x52, 0x30, 0xd0, 0x51, 0x40, 0x11,
    0xce, 0xcc, 0x3,  0xb,  0x1b, 0x2,  0x85, 0x3d, 0x4b, 0x52, 0x73, 0x8b,
    0x81, 0xec, 0xe8, 0x6a, 0x25, 0x4f, 0x17, 0x20, 0xad, 0x94, 0x96, 0x59,
    0x94, 0x5b, 0x9e, 0x58, 0x94, 0xaa, 0x6b, 0x60, 0x6e, 0x68, 0x6e, 0xa8,
    0x5b, 0x92, 0x5a, 0x5c, 0xa2, 0x57, 0x91, 0x9b, 0xa3, 0x57, 0x51, 0xa5,
    0x4,  0x54, 0xee, 0x98, 0x93, 0x99, 0x58, 0xec, 0x99, 0x2,  0xd6, 0x1,
    0x57, 0xa,  0x93, 0x8f, 0x5,  0x2a, 0x70, 0xca, 0xc9, 0x4f, 0x82, 0x9a,
    0xe7, 0x9d, 0x99, 0x97, 0x2,  0x64, 0x99, 0x0,  0x45, 0xdd, 0x72, 0x12,
    0xd3, 0x8b, 0xa1, 0x36, 0x86, 0x64, 0xe6, 0x2,  0xcd, 0x4c, 0xcc, 0x2d,
    0x80, 0x3a, 0xcc, 0x25, 0xb1, 0x24, 0x11, 0x64, 0x31, 0xc8, 0x26, 0xdd,
    0x14, 0x10, 0xa7, 0x56, 0x47, 0x1,  0xae, 0xdb, 0x90, 0x22, 0xdd, 0xc6,
    0x78, 0x74, 0x3,  0x7d, 0x67, 0x6c, 0x68, 0x61, 0x6c, 0x6c, 0x6c, 0x82,
    0xdd, 0x98, 0x58, 0x20, 0x4,  0x0,  0x5a, 0x30, 0x56, 0xa6, 0x4e, 0x1,
    0x0,  0x0};

// Used "hello world" as string
const uint8_t kGarbageTestChecksumData[] = {
    0x1f, 0x8b, 0x8,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x3,  0xcb,
    0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0x1,
    0x0,  0x85, 0x11, 0x4a, 0xd,  0xb,  0x0,  0x0,  0x0};

void RunResponseCallback(dbus::ObjectProxy::ResponseOrErrorCallback callback,
                         std::unique_ptr<dbus::Response> response) {
  if (response->GetMessageType() == DBUS_MESSAGE_TYPE_ERROR) {
    std::move(callback).Run(nullptr,
                            static_cast<dbus::ErrorResponse*>(response.get()));
  } else {
    std::move(callback).Run(response.get(), nullptr);
  }
}

class FakeUpdateObserver : public ash::firmware_update::mojom::UpdateObserver {
 public:
  void OnUpdateListChanged(
      std::vector<ash::firmware_update::mojom::FirmwareUpdatePtr>
          firmware_updates) override {
    updates_ = std::move(firmware_updates);
    ++num_times_notified_;
  }

  mojo::PendingRemote<ash::firmware_update::mojom::UpdateObserver>
  pending_remote() {
    return receiver_.BindNewPipeAndPassRemote();
  }

  const std::vector<ash::firmware_update::mojom::FirmwareUpdatePtr>& updates()
      const {
    return updates_;
  }

  int num_times_notified() { return num_times_notified_; }

 private:
  std::vector<ash::firmware_update::mojom::FirmwareUpdatePtr> updates_;
  mojo::Receiver<ash::firmware_update::mojom::UpdateObserver> receiver_{this};
  int num_times_notified_ = 0;
};

class FakeUpdateProgressObserver
    : public ash::firmware_update::mojom::UpdateProgressObserver {
 public:
  void OnStatusChanged(
      ash::firmware_update::mojom::InstallationProgressPtr update) override {
    update_ = std::move(update);
  }

  mojo::PendingRemote<ash::firmware_update::mojom::UpdateProgressObserver>
  pending_remote() {
    return receiver_.BindNewPipeAndPassRemote();
  }

  const ash::firmware_update::mojom::InstallationProgressPtr& GetLatestUpdate()
      const {
    return update_;
  }

 private:
  ash::firmware_update::mojom::InstallationProgressPtr update_;
  mojo::Receiver<ash::firmware_update::mojom::UpdateProgressObserver> receiver_{
      this};
};

class FakeDeviceRequestObserver
    : public ash::firmware_update::mojom::DeviceRequestObserver {
 public:
  void OnDeviceRequest(
      ash::firmware_update::mojom::DeviceRequestPtr request) override {
    request_ = std::move(request);
  }

  mojo::PendingRemote<ash::firmware_update::mojom::DeviceRequestObserver>
  pending_remote() {
    return receiver_.BindNewPipeAndPassRemote();
  }

  const ash::firmware_update::mojom::DeviceRequestPtr& GetLatestRequest()
      const {
    return request_;
  }

 private:
  ash::firmware_update::mojom::DeviceRequestPtr request_;
  mojo::Receiver<ash::firmware_update::mojom::DeviceRequestObserver> receiver_{
      this};
};

}  // namespace

namespace ash {

class FirmwareUpdateManagerTest : public testing::Test {
 public:
  FirmwareUpdateManagerTest() {
    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    bus_ = base::MakeRefCounted<dbus::MockBus>(options);

    dbus::ObjectPath fwupd_service_path(kFwupdServicePath);
    proxy_ = base::MakeRefCounted<NiceMock<dbus::MockObjectProxy>>(
        bus_.get(), kFwupdServiceName, fwupd_service_path);

    EXPECT_CALL(*bus_.get(),
                GetObjectProxy(kFwupdServiceName, fwupd_service_path))
        .WillRepeatedly(testing::Return(proxy_.get()));

    EXPECT_CALL(*proxy_, DoCallMethodWithErrorResponse(_, _, _))
        .WillRepeatedly(
            Invoke(this, &FirmwareUpdateManagerTest::OnMethodCalled));

    // Create a fake response for the call to SetFeatureFlags.
    dbus_responses_.push_back(dbus::Response::CreateEmpty());
    FwupdClient::Initialize(bus_.get());
    dbus_client_ = FwupdClient::Get();
    fake_fwupd_download_client_ = std::make_unique<FakeFwupdDownloadClient>();
    firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
    firmware_update_manager_->BindInterface(
        update_provider_remote_.BindNewPipeAndPassReceiver());
  }

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

  ~FirmwareUpdateManagerTest() override {
    if (message_center::MessageCenter::Get()) {
      message_center::MessageCenter::Shutdown();
    }
    // Destructor depends on FirmwareUpdateManager.
    firmware_update_notification_controller_.reset();
    // Destructor depends on FwupdClient.
    firmware_update_manager_.reset();
    FwupdClient::Shutdown();
    network_handler_test_helper_.reset();
  }

  void OnMethodCalled(dbus::MethodCall* method_call,
                      int timeout_ms,
                      dbus::ObjectProxy::ResponseOrErrorCallback* callback) {
    ASSERT_FALSE(dbus_responses_.empty());
    auto response = std::move(dbus_responses_.front());
    task_environment_.GetMainThreadTaskRunner()->PostTask(
        FROM_HERE, base::BindOnce(&RunResponseCallback, std::move(*callback),
                                  std::move(response)));
    dbus_responses_.pop_front();
  }

 protected:
  void SetUp() override {
    // Default behaviour is RefreshRemote will always be called
    PrepareForRefreshRemote();
  }

  void InitializeNotificationController() {
    message_center::MessageCenter::Initialize();
    firmware_update_notification_controller_ =
        std::make_unique<FirmwareUpdateNotificationController>(
            message_center());
    firmware_update_notification_controller_
        ->set_should_show_notification_for_test(/*show_notification=*/true);
  }

  void RequestDevices() {
    firmware_update_manager_->RequestDevices();
    task_environment_.RunUntilIdle();
  }

  void TriggerOnDeviceRequestResponse(
      firmware_update::mojom::DeviceRequestId id,
      firmware_update::mojom::DeviceRequestKind kind) {
    FwupdRequest request(static_cast<uint32_t>(id),
                         static_cast<uint32_t>(kind));
    firmware_update_manager_->OnDeviceRequestResponse(request);
    task_environment_.RunUntilIdle();
  }

  firmware_update::mojom::FirmwareUpdatePtr CreateFakeUpdate() {
    auto update = firmware_update::mojom::FirmwareUpdate::New();
    update->device_id = "id";
    update->device_name = base::UTF8ToUTF16(std::string("name"));
    update->device_version = "version";
    update->device_description = base::UTF8ToUTF16(std::string("description"));
    update->priority = firmware_update::mojom::UpdatePriority::kMedium;
    update->filepath = base::FilePath("filepath");
    update->checksum = "checksum";
    return update;
  }

  void TriggerInstallFailed() {
    // Create a fake update so that the following method call works correctly.
    firmware_update_manager_->inflight_update_ = CreateFakeUpdate();
    // Setting default value for failure error name
    FwupdDbusResult result = FwupdDbusResult::kInternalError;
    // Trigger an unsuccessful update.
    firmware_update_manager_->OnInstallResponse(
        base::BindOnce([](MethodResult) {}), result);
    task_environment_.RunUntilIdle();
  }

  void SetStatus(FwupdStatus fwupd_status) {
    SetProperties(/*percentage=*/0, static_cast<uint32_t>(fwupd_status));
  }

  void SetFakeUrlForTesting(const std::string& fake_url) {
    firmware_update_manager_->set_fake_url_for_testing(fake_url);
  }

  message_center::MessageCenter* message_center() const {
    return message_center::MessageCenter::Get();
  }

  std::unique_ptr<dbus::Response> CreateEmptyResponse() {
    return dbus::Response::CreateEmpty();
  }

  std::unique_ptr<dbus::Response> CreateEmptyDeviceResponse() {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateOneDeviceResponse() {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);
    dbus::MessageWriter dict_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kNameKey);
    dict_writer.AppendVariantOfString(kFakeDeviceNameForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kIdKey);
    dict_writer.AppendVariantOfString(kFakeDeviceIdForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateInternalDeviceResponse() {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);
    dbus::MessageWriter dict_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kNameKey);
    dict_writer.AppendVariantOfString(kFakeInternalDeviceNameForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kIdKey);
    dict_writer.AppendVariantOfString(kFakeInternalDeviceIdForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kFlagsKey);
    dict_writer.AppendVariantOfUint64(kFakeFlagForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kTrustFlagsKey);
    dict_writer.AppendVariantOfUint64(kFakeReportFlagForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateNumberOfDeviceResponses(
      int number_of_responses) {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);
    dbus::MessageWriter dict_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);

    for (int i = 0; i < number_of_responses; ++i) {
      response_array_writer.OpenArray("{sv}", &device_array_writer);
      device_array_writer.OpenDictEntry(&dict_writer);
      dict_writer.AppendString(kNameKey);
      dict_writer.AppendVariantOfString(
          base::StrCat({kFakeDeviceNameForTesting, base::NumberToString(i)}));
      device_array_writer.CloseContainer(&dict_writer);

      device_array_writer.OpenDictEntry(&dict_writer);
      dict_writer.AppendString(kIdKey);
      dict_writer.AppendVariantOfString(
          base::StrCat({kFakeDeviceIdForTesting, base::NumberToString(i)}));
      device_array_writer.CloseContainer(&dict_writer);
      response_array_writer.CloseContainer(&device_array_writer);
    }

    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateOneUpdateResponseWithPriority(
      uint32_t update_priority) {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);
    dbus::MessageWriter dict_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kDescriptionKey);
    dict_writer.AppendVariantOfString(kFakeUpdateDescriptionForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kVersionKey);
    dict_writer.AppendVariantOfString(kFakeUpdateVersionForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kPriorityKey);
    dict_writer.AppendVariantOfUint32(update_priority);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kUriKey);
    dict_writer.AppendVariantOfString(kFakeUpdateUriForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kChecksumKey);
    dict_writer.AppendVariantOfString(kEmptyFileSha256ForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kTrustFlagsKey);
    dict_writer.AppendVariantOfUint64(kFakeReportFlagForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateOneUpdateResponse() {
    return CreateOneUpdateResponseWithPriority(kFakeUpdatePriorityForTesting);
  }

  std::unique_ptr<dbus::Response> CreateOneCriticalUpdateResponse() {
    return CreateOneUpdateResponseWithPriority(
        kFakeCriticalUpdatePriorityForTesting);
  }

  std::unique_ptr<dbus::Response> CreateOneUpdateResponseWithChecksum(
      const std::string& checksum) {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);
    dbus::MessageWriter dict_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kDescriptionKey);
    dict_writer.AppendVariantOfString(kFakeUpdateDescriptionForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kVersionKey);
    dict_writer.AppendVariantOfString(kFakeUpdateVersionForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kPriorityKey);
    dict_writer.AppendVariantOfUint32(kFakeUpdatePriorityForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kUriKey);
    dict_writer.AppendVariantOfString(kFakeUpdateUriForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kChecksumKey);
    dict_writer.AppendVariantOfString(checksum);
    device_array_writer.CloseContainer(&dict_writer);

    device_array_writer.OpenDictEntry(&dict_writer);
    dict_writer.AppendString(kTrustFlagsKey);
    dict_writer.AppendVariantOfUint64(kFakeReportFlagForTesting);
    device_array_writer.CloseContainer(&dict_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateNoUpdateResponse() {
    auto response = dbus::Response::CreateEmpty();

    dbus::MessageWriter response_writer(response.get());
    dbus::MessageWriter response_array_writer(nullptr);
    dbus::MessageWriter device_array_writer(nullptr);

    // The response is an array of arrays of dictionaries. Each dictionary is
    // one device description.
    response_writer.OpenArray("a{sv}", &response_array_writer);
    response_array_writer.OpenArray("{sv}", &device_array_writer);

    response_array_writer.CloseContainer(&device_array_writer);
    response_writer.CloseContainer(&response_array_writer);

    return response;
  }

  std::unique_ptr<dbus::Response> CreateBoolResponse(bool success) {
    auto response = dbus::Response::CreateEmpty();
    dbus::MessageWriter response_writer(response.get());
    response_writer.AppendBool(success);
    return response;
  }

  std::unique_ptr<dbus::ErrorResponse> CreateErrorResponse() {
    DBusMessage* raw_message = dbus_message_new(DBUS_MESSAGE_TYPE_ERROR);
    return dbus::ErrorResponse::FromRawMessage(raw_message);
  }

  std::unique_ptr<dbus::ErrorResponse> CreateErrorResponseWithName(
      const std::string& name) {
    auto raw_message = CreateErrorResponse();
    raw_message->SetErrorName(name);
    return raw_message;
  }

  void CreateOneDeviceAndUpdateResponse() {
    dbus_responses_.push_back(CreateOneDeviceResponse());
    dbus_responses_.push_back(CreateOneUpdateResponse());
  }

  void SetupObserver(FakeUpdateObserver* observer) {
    firmware_update_manager_->ObservePeripheralUpdates(
        observer->pending_remote());
    task_environment_.RunUntilIdle();
  }

  network::TestURLLoaderFactory& GetTestUrlLoaderFactory() {
    return fake_fwupd_download_client_->test_url_loader_factory();
  }

  void SetupProgressObserver(FakeUpdateProgressObserver* observer) {
    install_controller_remote_->AddUpdateProgressObserver(
        observer->pending_remote());
    task_environment_.RunUntilIdle();
  }

  void SetupDeviceRequestObserver(FakeDeviceRequestObserver* observer) {
    install_controller_remote_->AddDeviceRequestObserver(
        observer->pending_remote());
    task_environment_.RunUntilIdle();
  }

  void PrepareForRefreshRemote() {
    firmware_update_manager_->set_refresh_remote_for_testing(true);
    // Add default response for downloading checksum and firmware file for
    // RefreshRemote logic.
    std::string data(reinterpret_cast<const char*>(kTestChecksumData),
                     std::size(kTestChecksumData));
    GetTestUrlLoaderFactory().AddResponse(kChecksumFileUriForTesting, data);
    GetTestUrlLoaderFactory().AddResponse(kFirmwareFileUriForTesting, "");
    dbus_responses_.push_back(CreateEmptyResponse());
  }

  void PrepareForIncorrectKeyJsonRefreshRemote() {
    std::string data(reinterpret_cast<const char*>(kIncorrectTestChecksumData),
                     std::size(kIncorrectTestChecksumData));
    GetTestUrlLoaderFactory().AddResponse(kChecksumFileUriForTesting, data);
  }

  void PrepareForIncorrectFormatRefreshRemote() {
    std::string data(reinterpret_cast<const char*>(kGarbageTestChecksumData),
                     std::size(kGarbageTestChecksumData));
    GetTestUrlLoaderFactory().AddResponse(kChecksumFileUriForTesting, data);
  }

  bool PrepareForUpdate(const std::string& device_id) {
    base::test::TestFuture<
        mojo::PendingRemote<ash::firmware_update::mojom::InstallController>>
        pending_remote_future;
    update_provider_remote_->PrepareForUpdate(
        device_id, pending_remote_future.GetCallback());
    auto pending_remote = pending_remote_future.Take();
    if (!pending_remote.is_valid()) {
      return false;
    }

    install_controller_remote_.Bind(std::move(pending_remote));
    task_environment_.RunUntilIdle();
    return true;
  }

  void SetProperties(uint32_t percentage, uint32_t status) {
    dbus_client_->SetPropertiesForTesting(percentage, status);
    task_environment_.RunUntilIdle();
  }

  void BeginUpdate(const std::string& device_id,
                   const base::FilePath& filepath) {
    firmware_update_manager_->BeginUpdate(device_id, filepath);
    task_environment_.RunUntilIdle();
  }

  void RequestAllUpdates() {
    firmware_update_manager_->RequestAllUpdates(
        FirmwareUpdateManager::Source::kInstallComplete);
  }

  void RequestAllUpdatesFromSource(FirmwareUpdateManager::Source source) {
    firmware_update_manager_->RequestAllUpdates(source);
  }

  void AdvanceClock(base::TimeDelta time) {
    task_environment_.AdvanceClock(time);
  }

  void SetCellularService() {
    network_handler_test_helper_->ClearServices();
    network_handler_test_helper_->ConfigureService(
        R"({"GUID": "cellular_guid", "Type": "cellular", "Technology": "LTE",
            "State": "online"})");
    task_environment_.RunUntilIdle();
  }

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
  // `FwupdClient` must be be before `FirmwareUpdateManager`.
  raw_ptr<FwupdClient, DanglingUntriaged> dbus_client_ = nullptr;
  std::unique_ptr<FakeFwupdDownloadClient> fake_fwupd_download_client_;
  std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
  // `FirmwareUpdateNotificationController` must be be after
  // `FirmwareUpdateManager` to ensure that
  // `FirmwareUpdateNotificationController` is removed as an observer before
  // `FirmwareUpdateManager` is destroyed.
  std::unique_ptr<FirmwareUpdateNotificationController>
      firmware_update_notification_controller_;
  mojo::Remote<ash::firmware_update::mojom::UpdateProvider>
      update_provider_remote_;
  mojo::Remote<ash::firmware_update::mojom::InstallController>
      install_controller_remote_;

  // Mock bus for simulating calls.
  scoped_refptr<dbus::MockBus> bus_;
  scoped_refptr<NiceMock<dbus::MockObjectProxy>> proxy_;

  // Fake responses.
  std::deque<std::unique_ptr<dbus::Response>> dbus_responses_;
};

TEST_F(FirmwareUpdateManagerTest, CorrectMockInstance) {
  EXPECT_EQ(dbus_client_, FwupdClient::Get());
}

TEST_F(FirmwareUpdateManagerTest, RequestAllUpdatesNoDevices) {
  dbus_responses_.push_back(CreateEmptyDeviceResponse());
  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  EXPECT_TRUE(updates.empty());
  ASSERT_EQ(0U, firmware_update_manager_->GetUpdateCount());
}

TEST_F(FirmwareUpdateManagerTest, RequestAllUpdatesOneDeviceNoUpdates) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateNoUpdateResponse());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  EXPECT_TRUE(updates.empty());
  ASSERT_EQ(0U, firmware_update_manager_->GetUpdateCount());
}

TEST_F(FirmwareUpdateManagerTest, RequestAllUpdatesOneDeviceOneUpdate) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());
  EXPECT_EQ(kFakeDeviceIdForTesting, updates[0]->device_id);
  EXPECT_EQ(base::UTF8ToUTF16(std::string(kFakeDeviceNameForTesting)),
            updates[0]->device_name);
  EXPECT_EQ(kFakeUpdateVersionForTesting, updates[0]->device_version);
  EXPECT_EQ(base::UTF8ToUTF16(std::string(kFakeUpdateDescriptionForTesting)),
            updates[0]->device_description);
  EXPECT_EQ(ash::firmware_update::mojom::UpdatePriority(
                kFakeUpdatePriorityForTesting),
            updates[0]->priority);
  EXPECT_EQ(kFakeUpdateUriForTesting, updates[0]->filepath.value());
}

TEST_F(FirmwareUpdateManagerTest, RequestUpdatesClearsCache) {
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  CreateOneDeviceAndUpdateResponse();

  RequestDevices();

  // Expect cache to clear and only 1 updates now instead of 2.
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& new_updates =
      update_observer.updates();
  ASSERT_EQ(1U, new_updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());
}

TEST_F(FirmwareUpdateManagerTest, RequestAllUpdatesTwoDeviceOneWithUpdate) {
  dbus_responses_.push_back(CreateNumberOfDeviceResponses(2));
  dbus_responses_.push_back(CreateNoUpdateResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  // The second device was the one with the update.
  EXPECT_EQ(std::string(kFakeDeviceIdForTesting) + "1", updates[0]->device_id);
  EXPECT_EQ(base::UTF8ToUTF16(std::string(kFakeDeviceNameForTesting) + "1"),
            updates[0]->device_name);
  EXPECT_EQ(kFakeUpdateVersionForTesting, updates[0]->device_version);
  EXPECT_EQ(base::UTF8ToUTF16(std::string(kFakeUpdateDescriptionForTesting)),
            updates[0]->device_description);
  EXPECT_EQ(ash::firmware_update::mojom::UpdatePriority(
                kFakeUpdatePriorityForTesting),
            updates[0]->priority);
}

TEST_F(FirmwareUpdateManagerTest, RequestUpdatesMultipleTimes) {
  dbus_responses_.push_back(CreateNumberOfDeviceResponses(2));
  dbus_responses_.push_back(CreateNoUpdateResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  ASSERT_EQ(1, update_observer.num_times_notified());
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  // Request all updates multiple times, this time while a request is already
  // being made.
  PrepareForRefreshRemote();
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  RequestAllUpdates();
  RequestAllUpdates();
  task_environment_.RunUntilIdle();
  // Expect only one additional RequestAllUpdates() to go through.
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(2, update_observer.num_times_notified());

  // Now request all updates again, this time after the previous request has
  // been completed.
  PrepareForRefreshRemote();
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  RequestAllUpdates();
  task_environment_.RunUntilIdle();
  // Expect another additional RequestAllUpdates() to go through.
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(3, update_observer.num_times_notified());
}

TEST_F(FirmwareUpdateManagerTest, BeginUpdate) {
  base::HistogramTester histogram_tester;

  // Provide one device and update for RequestUpdates() call from SetupObserver.
  CreateOneDeviceAndUpdateResponse();
  // InstallUpdate success response.
  dbus_responses_.push_back(dbus::Response::CreateEmpty());
  // For RequestAllUpdates() call after install completes.
  PrepareForRefreshRemote();
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  ASSERT_EQ(1, update_observer.num_times_notified());

  const std::string fake_url =
      std::string("https://faketesturl/") + kFakeUpdateFileNameForTesting;
  SetFakeUrlForTesting(fake_url);
  GetTestUrlLoaderFactory().AddResponse(fake_url, "");

  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);

  BeginUpdate(std::string(kFakeDeviceIdForTesting), base::FilePath(fake_url));

  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kSuccess,
            update_progress_observer.GetLatestUpdate()->state);
  // Expect RequestAllUpdates() to have been called after an install to refresh
  // the update list.
  ASSERT_EQ(2, update_observer.num_times_notified());

  histogram_tester.ExpectUniqueSample("ChromeOS.FirmwareUpdateUi.InstallResult",
                                      MethodResult::kSuccess, 1);
}

struct FailedMethodWithErrorParam {
  explicit FailedMethodWithErrorParam(std::string error_name,
                                      MethodResult install_result)
      : error_name(error_name), install_result(install_result) {}

  std::string error_name;
  MethodResult install_result;
};

class FirmwareUpdateManagerTest_FailedMethodWithErrorMessage
    : public FirmwareUpdateManagerTest,
      public testing::WithParamInterface<FailedMethodWithErrorParam> {};

INSTANTIATE_TEST_SUITE_P(
    FirmwareUpdateManagerTest_FailedMethodWithErrorMessage,
    FirmwareUpdateManagerTest_FailedMethodWithErrorMessage,
    ::testing::Values(
        FailedMethodWithErrorParam(kFwupdErrorName_Internal,
                                   MethodResult::kInternalError),
        FailedMethodWithErrorParam(kFwupdErrorName_VersionNewer,
                                   MethodResult::kVersionNewerError),
        FailedMethodWithErrorParam(kFwupdErrorName_VersionSame,
                                   MethodResult::kVersionSameError),
        FailedMethodWithErrorParam(kFwupdErrorName_AlreadyPending,
                                   MethodResult::kAlreadyPendingError),
        FailedMethodWithErrorParam(kFwupdErrorName_AuthFailed,
                                   MethodResult::kAuthFailedError),
        FailedMethodWithErrorParam(kFwupdErrorName_Read,
                                   MethodResult::kReadError),
        FailedMethodWithErrorParam(kFwupdErrorName_Write,
                                   MethodResult::kWriteError),
        FailedMethodWithErrorParam(kFwupdErrorName_InvalidFile,
                                   MethodResult::kInvalidFileError),
        FailedMethodWithErrorParam(kFwupdErrorName_NotFound,
                                   MethodResult::kNotFoundError),
        FailedMethodWithErrorParam(kFwupdErrorName_NothingToDo,
                                   MethodResult::kNothingToDoError),
        FailedMethodWithErrorParam(kFwupdErrorName_NotSupported,
                                   MethodResult::kNotSupportedError),
        FailedMethodWithErrorParam(kFwupdErrorName_SignatureInvalid,
                                   MethodResult::kSignatureInvalidError),
        FailedMethodWithErrorParam(kFwupdErrorName_AcPowerRequired,
                                   MethodResult::kAcPowerRequiredError),
        FailedMethodWithErrorParam(kFwupdErrorName_PermissionDenied,
                                   MethodResult::kPermissionDeniedError),
        FailedMethodWithErrorParam(kFwupdErrorName_BrokenSystem,
                                   MethodResult::kBrokenSystemError),
        FailedMethodWithErrorParam(kFwupdErrorName_BatteryLevelTooLow,
                                   MethodResult::kBatteryLevelTooLowError),
        FailedMethodWithErrorParam(kFwupdErrorName_NeedsUserAction,
                                   MethodResult::kNeedsUserActionError),
        FailedMethodWithErrorParam(kFwupdErrorName_AuthExpired,
                                   MethodResult::kAuthExpiredError),
        FailedMethodWithErrorParam("Random Error", MethodResult::kUnknownError),
        FailedMethodWithErrorParam("Random Error 2",
                                   MethodResult::kUnknownError)));

TEST_P(FirmwareUpdateManagerTest_FailedMethodWithErrorMessage,
       FailedInstall_WithErrorMessage) {
  base::HistogramTester histogram_tester;

  // Provide one device and update for RequestUpdates() call from SetupObserver.
  CreateOneDeviceAndUpdateResponse();
  // InstallUpdate failed response.
  dbus_responses_.push_back(CreateErrorResponseWithName(GetParam().error_name));
  // For RequestAllUpdates() call after install completes.
  PrepareForRefreshRemote();
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  ASSERT_EQ(1, update_observer.num_times_notified());

  const std::string fake_url =
      std::string("https://faketesturl/") + kFakeUpdateFileNameForTesting;
  SetFakeUrlForTesting(fake_url);
  GetTestUrlLoaderFactory().AddResponse(fake_url, "");

  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);

  BeginUpdate(std::string(kFakeDeviceIdForTesting), base::FilePath(fake_url));

  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kFailed,
            update_progress_observer.GetLatestUpdate()->state);
  // Expect RequestAllUpdates() to have been called after an install to refresh
  // the update list.
  ASSERT_EQ(2, update_observer.num_times_notified());

  histogram_tester.ExpectUniqueSample("ChromeOS.FirmwareUpdateUi.InstallResult",
                                      GetParam().install_result, 1);
}

TEST_P(FirmwareUpdateManagerTest_FailedMethodWithErrorMessage,
       FailedRefreshRemote_WithErrorMessage) {
  base::HistogramTester histogram_tester;

  // Clear default "empty" dbus response expected from RefreshRemote in SetUp()
  dbus_responses_.clear();
  // Set RefreshRemote failed response.
  dbus_responses_.push_back(CreateErrorResponseWithName(GetParam().error_name));
  // Provide one device and update for RequestUpdates() call from SetupObserver.
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  // RequestAllUpdates is called even after RefreshRemote failed with error
  ASSERT_EQ(1, update_observer.num_times_notified());

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult",
      GetParam().install_result, 1);
}

TEST_F(FirmwareUpdateManagerTest, BeginUpdateLocalPatch) {
  base::HistogramTester histogram_tester;

  // Provide one device and update for RequestUpdates() call from SetupObserver.
  CreateOneDeviceAndUpdateResponse();
  // InstallUpdate success response.
  dbus_responses_.push_back(dbus::ErrorResponse::CreateEmpty());
  // For RequestAllUpdates() call after install completes.
  PrepareForRefreshRemote();
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);

  base::FilePath root_dir;
  CHECK(base::PathService::Get(base::DIR_TEMP, &root_dir));
  const base::FilePath root_path =
      root_dir.Append(FILE_PATH_LITERAL(kDownloadDir))
          .Append(FILE_PATH_LITERAL(kCacheDir));
  const std::string test_filename =
      std::string(kFakeDeviceIdForTesting) + std::string(kCabExtension);
  base::FilePath full_path = root_path.Append(test_filename);
  // Create a temporary file to simulate a .cab available for install.
  base::WriteFile(full_path, "");
  EXPECT_TRUE(base::PathExists(full_path));
  const std::string uri = kFilePathIdentifier + full_path.value();

  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);
  BeginUpdate(std::string(kFakeDeviceIdForTesting), base::FilePath(uri));

  histogram_tester.ExpectUniqueSample("ChromeOS.FirmwareUpdateUi.InstallResult",
                                      MethodResult::kSuccess, 1);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kSuccess,
            update_progress_observer.GetLatestUpdate()->state);
}

TEST_F(FirmwareUpdateManagerTest, BeginUpdateInvalidFile) {
  base::HistogramTester histogram_tester;

  // Provide one device and update for RequestUpdates() call from SetupObserver.
  CreateOneDeviceAndUpdateResponse();
  // InstallUpdateResponse.
  dbus_responses_.push_back(dbus::Response::CreateEmpty());
  // For RequestAllUpdates() call after install completes.
  PrepareForRefreshRemote();
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);

  std::string fake_url = "https://faketesturl/";
  SetFakeUrlForTesting(fake_url);
  GetTestUrlLoaderFactory().AddResponse(fake_url, "");

  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);
  BeginUpdate(std::string(kFakeDeviceIdForTesting),
              base::FilePath("BadTestFilename@#.cab"));

  histogram_tester.ExpectUniqueSample("ChromeOS.FirmwareUpdateUi.InstallResult",
                                      MethodResult::kInvalidPatchFile, 1);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kFailed,
            update_progress_observer.GetLatestUpdate()->state);
}

TEST_F(FirmwareUpdateManagerTest, OnPropertiesChangedResponse) {
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);

  // Initial state.
  SetProperties(/**percentage=*/0u, /**status=*/0u);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUnknown,
            update_progress_observer.GetLatestUpdate()->state);
  EXPECT_EQ(0u, update_progress_observer.GetLatestUpdate()->percentage);
  // Install in progress.
  SetProperties(/**percentage=*/1u, /**status=*/5u);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUpdating,
            update_progress_observer.GetLatestUpdate()->state);
  EXPECT_EQ(1u, update_progress_observer.GetLatestUpdate()->percentage);
  // Waiting for user action.
  SetProperties(/**percentage=*/25u, /**status=*/14u);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kWaitingForUser,
            update_progress_observer.GetLatestUpdate()->state);
  EXPECT_EQ(25u, update_progress_observer.GetLatestUpdate()->percentage);
  // Device restarting
  SetProperties(/**percentage=*/100u, /**status=*/4u);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kRestarting,
            update_progress_observer.GetLatestUpdate()->state);
  EXPECT_EQ(100u, update_progress_observer.GetLatestUpdate()->percentage);

  // Emitted once install is completed and device has been restarted.
  SetProperties(/**percentage=*/100u, /**status=*/0u);
  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUnknown,
            update_progress_observer.GetLatestUpdate()->state);
  EXPECT_EQ(100u, update_progress_observer.GetLatestUpdate()->percentage);
}

TEST_F(FirmwareUpdateManagerTest, InvalidChecksum) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponseWithChecksum(
      "badbbadbad1ef97238fb24c5e40a979bc544bb2b0967b863e43e7d58e0d9a923f"));
  dbus_responses_.push_back(dbus::Response::CreateEmpty());
  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  ASSERT_EQ(1, update_observer.num_times_notified());

  // No updates available since checksum is empty.
  const auto& updates = update_observer.updates();
  ASSERT_TRUE(updates.empty());
}

TEST_F(FirmwareUpdateManagerTest, EmptyChecksum) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponseWithChecksum(""));
  dbus_responses_.push_back(dbus::Response::CreateEmpty());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  ASSERT_EQ(1, update_observer.num_times_notified());

  // No updates available since checksum is empty.
  const auto& updates = update_observer.updates();
  ASSERT_TRUE(updates.empty());
}

TEST_F(FirmwareUpdateManagerTest, WrongChecksumVariant) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponseWithChecksum(
      "badbbadbad1ef97238fb24c5e40a979bc544bb2b"));
  dbus_responses_.push_back(dbus::Response::CreateEmpty());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  ASSERT_EQ(1, update_observer.num_times_notified());

  // No updates available since checksum is empty.
  const auto& updates = update_observer.updates();
  ASSERT_TRUE(updates.empty());
}

TEST_F(FirmwareUpdateManagerTest, NotificationShownForCriticalUpdate) {
  InitializeNotificationController();

  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneCriticalUpdateResponse());
  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
  message_center()->RemoveNotification(kFirmwareUpdateNotificationId,
                                       /*by_user=*/true);

  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneCriticalUpdateResponse());
  RequestDevices();

  // Request updates again and verify that the notification is not being
  // shown multiple times for the same update.
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}

TEST_F(FirmwareUpdateManagerTest, NotificationNotShownIfNoCriticalUpdates) {
  InitializeNotificationController();
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}

TEST_F(FirmwareUpdateManagerTest, DeviceCountMetric) {
  base::HistogramTester histogram_tester;

  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnStartup.DeviceCount", 1, 1);
  RequestDevices();
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnRefresh.DeviceCount", 1, 1);
}

TEST_F(FirmwareUpdateManagerTest, UpdateCountMetric) {
  base::HistogramTester histogram_tester;

  dbus_responses_.push_back(CreateNumberOfDeviceResponses(3));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(1));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(1));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(3));

  // Create a duplicate of the above responses since we're calling
  // RequestDevices during this test.
  dbus_responses_.push_back(CreateNumberOfDeviceResponses(3));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(1));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(1));
  dbus_responses_.push_back(CreateOneUpdateResponseWithPriority(3));

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnStartup.CriticalUpdateCount", 1, 1);
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnStartup.NonCriticalUpdateCount", 2, 1);

  // Before requesting devices, "OnRefresh" metrics should be empty.
  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.OnRefresh.CriticalUpdateCount", 0);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.OnRefresh.NonCriticalUpdateCount", 0);

  RequestDevices();
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnRefresh.CriticalUpdateCount", 1, 1);
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.OnRefresh.NonCriticalUpdateCount", 2, 1);
}

TEST_F(FirmwareUpdateManagerTest, InternalDeviceFiltered) {
  dbus_responses_.push_back(CreateOneDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());
  dbus_responses_.push_back(CreateInternalDeviceResponse());
  dbus_responses_.push_back(CreateOneUpdateResponse());

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  ASSERT_EQ(1U, updates.size());
  EXPECT_EQ(kFakeDeviceIdForTesting, updates[0]->device_id);
}

TEST_F(FirmwareUpdateManagerTest, SetupDeviceRequestObserver) {
  // Simple test to ensure that binding the observer works.
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);
}

TEST_F(FirmwareUpdateManagerTest, DeviceRequestObserver) {
  base::HistogramTester histogram_tester;
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);

  // For each combination of DeviceRequestId and DeviceRequestKind, call
  // OnDeviceRequestResponse on firmware_update_manager and then verify that the
  // observer received the correct DeviceRequest.
  int device_request_id_size =
      static_cast<int>(firmware_update::mojom::DeviceRequestId::kMaxValue) + 1;
  int device_request_kind_size =
      static_cast<int>(firmware_update::mojom::DeviceRequestKind::kMaxValue) +
      1;

  for (int id_index = 0; id_index < device_request_id_size; id_index++) {
    firmware_update::mojom::DeviceRequestId id =
        static_cast<firmware_update::mojom::DeviceRequestId>(id_index);
    for (int kind_index = 0; kind_index < device_request_kind_size;
         kind_index++) {
      firmware_update::mojom::DeviceRequestKind kind =
          static_cast<firmware_update::mojom::DeviceRequestKind>(kind_index);

      TriggerOnDeviceRequestResponse(id, kind);

      EXPECT_EQ(id, device_request_observer.GetLatestRequest()->id);
      EXPECT_EQ(kind, device_request_observer.GetLatestRequest()->kind);
    }
    histogram_tester.ExpectBucketCount(
        "ChromeOS.FirmwareUpdateUi.RequestReceived.KindImmediate", id, 1);
    histogram_tester.ExpectBucketCount(
        "ChromeOS.FirmwareUpdateUi.RequestReceived.KindPost", id, 1);
    histogram_tester.ExpectBucketCount(
        "ChromeOS.FirmwareUpdateUi.RequestReceived.KindUnknown", id, 1);
  }
  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.RequestReceived.KindImmediate",
      device_request_id_size);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.RequestReceived.KindPost",
      device_request_id_size);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.RequestReceived.KindUnknown",
      device_request_id_size);
}

TEST_F(FirmwareUpdateManagerTest, DeviceRequestObserverMetrics) {
  base::HistogramTester histogram_tester;
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);

  TriggerOnDeviceRequestResponse(
      firmware_update::mojom::DeviceRequestId::kPressUnlock,
      firmware_update::mojom::DeviceRequestKind::kImmediate);

  EXPECT_EQ(firmware_update::mojom::DeviceRequestId::kPressUnlock,
            device_request_observer.GetLatestRequest()->id);
  EXPECT_EQ(firmware_update::mojom::DeviceRequestKind::kImmediate,
            device_request_observer.GetLatestRequest()->kind);

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.RequestReceived.KindImmediate",
      firmware_update::mojom::DeviceRequestId::kPressUnlock, 1);
}

struct FailedInstallParam {
  explicit FailedInstallParam(FwupdStatus fwupd_status)
      : fwupd_status(fwupd_status) {}

  FwupdStatus fwupd_status;
};

class FirmwareUpdateManagerTest_FailedInstall
    : public FirmwareUpdateManagerTest,
      public testing::WithParamInterface<FailedInstallParam> {};

INSTANTIATE_TEST_SUITE_P(
    FirmwareUpdateManagerTest_FailedInstall,
    FirmwareUpdateManagerTest_FailedInstall,
    ::testing::Values(FailedInstallParam(FwupdStatus::kUnknown),
                      FailedInstallParam(FwupdStatus::kIdle),
                      FailedInstallParam(FwupdStatus::kLoading),
                      FailedInstallParam(FwupdStatus::kDecompressing),
                      FailedInstallParam(FwupdStatus::kDeviceRestart),
                      FailedInstallParam(FwupdStatus::kDeviceWrite),
                      FailedInstallParam(FwupdStatus::kDeviceVerify),
                      FailedInstallParam(FwupdStatus::kScheduling),
                      FailedInstallParam(FwupdStatus::kDownloading),
                      FailedInstallParam(FwupdStatus::kDeviceRead),
                      FailedInstallParam(FwupdStatus::kDeviceErase),
                      FailedInstallParam(FwupdStatus::kWaitingForAuth),
                      FailedInstallParam(FwupdStatus::kDeviceBusy),
                      FailedInstallParam(FwupdStatus::kShutdown),
                      FailedInstallParam(FwupdStatus::kWaitingForUser)));

TEST_P(FirmwareUpdateManagerTest_FailedInstall, FailedInstall_WaitingForUser) {
  base::HistogramTester histogram_tester;

  // These three steps are necessary for SetStatus and TriggerInstallFailed to
  // work correctly.
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);

  SetStatus(GetParam().fwupd_status);
  TriggerInstallFailed();

  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.InstallFailedWithStatus", 1);
  histogram_tester.ExpectUniqueSample(
      "ChromeOS.FirmwareUpdateUi.InstallFailedWithStatus",
      GetParam().fwupd_status, 1);
}

TEST_F(FirmwareUpdateManagerTest, FailedInstall_DurationMetrics_MetricLogged) {
  base::HistogramTester histogram_tester;

  // These steps are necessary for the rest of the test to work correctly.
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);

  // Trigger a request.
  TriggerOnDeviceRequestResponse(
      firmware_update::mojom::DeviceRequestId::kInsertUSBCable,
      firmware_update::mojom::DeviceRequestKind::kImmediate);
  // Set status to kWaitingForUser, since that normally happens simultaneously
  // with device requests.
  SetStatus(FwupdStatus::kWaitingForUser);

  const std::string metric_name =
      "ChromeOS.FirmwareUpdateUi.InstallFailedWithDurationAfterRequest."
      "RequestIdInsertUSBCable";

  // Before the install fails, this metric shouldn't be logged.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 0);

  // Wait 10 seconds.
  AdvanceClock(base::Seconds(10));

  // Before the install fails, this metric shouldn't be logged.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 0);

  TriggerInstallFailed();

  // Expect that the metric is logged with the correct time.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 1);
}

TEST_F(FirmwareUpdateManagerTest,
       FailedInstall_DurationMetrics_MetricNotLogged) {
  base::HistogramTester histogram_tester;

  // These steps are necessary for the rest of the test to work correctly.
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);

  // Trigger a request.
  TriggerOnDeviceRequestResponse(
      firmware_update::mojom::DeviceRequestId::kInsertUSBCable,
      firmware_update::mojom::DeviceRequestKind::kImmediate);
  // Set status to kWaitingForUser, since that normally happens simultaneously
  // with device requests.
  SetStatus(FwupdStatus::kWaitingForUser);

  const std::string metric_name =
      "ChromeOS.FirmwareUpdateUi.FailedRequestDuration.RequestIdInsertUSBCable";

  // Before the install fails, this metric shouldn't be logged.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 0);

  // Wait 10 seconds.
  AdvanceClock(base::Seconds(10));

  // Before the install fails, this metric shouldn't be logged.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 0);

  // Set status to something other than kWaitingForUser, which indicates that
  // the user successfully fulfilled the request.
  SetStatus(FwupdStatus::kDeviceRestart);

  // Since there was a successful request, this metric that tracks the duration
  // of successful requests should have been recorded.
  histogram_tester.ExpectTimeBucketCount(
      "ChromeOS.FirmwareUpdateUi.RequestSucceededWithDuration."
      "RequestIdInsertUSBCable",
      base::Seconds(10), 1);

  TriggerInstallFailed();

  // Expect that the metric is not logged, because the request was successful,
  // even though the install failed.
  histogram_tester.ExpectTimeBucketCount(metric_name, base::Seconds(10), 0);
}

TEST_F(FirmwareUpdateManagerTest, RequestSucceededWithDurationMetric) {
  base::HistogramTester histogram_tester;

  // These steps are necessary for the rest of the test to work correctly.
  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
  FakeUpdateProgressObserver update_progress_observer;
  SetupProgressObserver(&update_progress_observer);
  FakeDeviceRequestObserver device_request_observer;
  SetupDeviceRequestObserver(&device_request_observer);

  // Trigger a request.
  TriggerOnDeviceRequestResponse(
      firmware_update::mojom::DeviceRequestId::kPressUnlock,
      firmware_update::mojom::DeviceRequestKind::kImmediate);
  // Set status to kWaitingForUser, since that normally happens simultaneously
  // with device requests.
  SetStatus(FwupdStatus::kWaitingForUser);

  const std::string request_success_metric_name =
      "ChromeOS.FirmwareUpdateUi.RequestSucceededWithDuration."
      "RequestIdPressUnlock";

  // The metric should not be logged yet.
  histogram_tester.ExpectTimeBucketCount(request_success_metric_name,
                                         base::Minutes(5), 0);

  // Wait 5 minutes.
  AdvanceClock(base::Minutes(5));

  // Set status to something other than kWaitingForUser, which indicates that
  // the user successfully fulfilled the request.
  SetStatus(FwupdStatus::kDeviceWrite);

  // Now the metric should be logged, since there was a successful request.
  histogram_tester.ExpectTimeBucketCount(request_success_metric_name,
                                         base::Minutes(5), 1);

  // Setting the status to something else now shouldn't trigger another metric
  // recording.
  SetStatus(FwupdStatus::kDownloading);
  // The metric should have the same number of samples since it didn't get
  // recorded again.
  histogram_tester.ExpectTimeBucketCount(request_success_metric_name,
                                         base::Minutes(5), 1);
}

TEST_F(FirmwareUpdateManagerTest, RefreshRemoteMeteredConnectionFromUI) {
  base::HistogramTester histogram_tester;

  // Provide one device and one update for RequestUpdates() call from
  // SetupObserver.
  CreateOneDeviceAndUpdateResponse();

  // Set connection to cellular (metered)
  SetCellularService();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  // Verify updates were caught even though RefreshRemote ran
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  histogram_tester.ExpectBucketCount(
      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult", MethodResult::kSuccess,
      1);
}

TEST_F(FirmwareUpdateManagerTest,
       RefreshRemoteMeteredConnectionFromOtherSource) {
  base::HistogramTester histogram_tester;

  // Clear default empty dbus response expected from RefreshRemote in SetUp()
  dbus_responses_.clear();
  // Provide one device for RequestUpdates() call twice
  CreateOneDeviceAndUpdateResponse();

  // Set connection to cellular (metered)
  SetCellularService();

  RequestAllUpdatesFromSource(FirmwareUpdateManager::Source::kStartup);
  task_environment_.RunUntilIdle();
  // Verify updates were caught even though RefreshRemote was skipped
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  CreateOneDeviceAndUpdateResponse();
  RequestAllUpdatesFromSource(FirmwareUpdateManager::Source::kUSBChange);
  task_environment_.RunUntilIdle();
  ASSERT_EQ(1U, firmware_update_manager_->GetUpdateCount());

  histogram_tester.ExpectTotalCount(
      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult", 0);
}

TEST_F(FirmwareUpdateManagerTest, RefreshRemoteIncorrectJsonKey) {
  base::HistogramTester histogram_tester;

  dbus_responses_.clear();
  PrepareForIncorrectKeyJsonRefreshRemote();
  // Provide one device and one update for RequestUpdates() call from
  // SetupObserver.
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  // Verify updates were caught even though RefreshRemote got an error
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1, update_observer.num_times_notified());

  histogram_tester.ExpectBucketCount(
      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult",
      MethodResult::kFailedToGetFirmwareFilename, 1);
}

TEST_F(FirmwareUpdateManagerTest, RefreshRemoteIncorrectChecksumFormat) {
  base::HistogramTester histogram_tester;

  dbus_responses_.clear();
  PrepareForIncorrectFormatRefreshRemote();
  // Provide one device and one update for RequestUpdates() call from
  // SetupObserver.
  CreateOneDeviceAndUpdateResponse();

  FakeUpdateObserver update_observer;
  SetupObserver(&update_observer);
  const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
      update_observer.updates();

  // Verify updates were caught even though RefreshRemote got an error
  ASSERT_EQ(1U, updates.size());
  ASSERT_EQ(1, update_observer.num_times_notified());

  histogram_tester.ExpectBucketCount(
      "ChromeOS.FirmwareUpdateUi.RefreshRemoteResult",
      MethodResult::kFailedToGetFirmwareFilename, 1);
}

}  // namespace ash