chromium/chromeos/ash/components/disks/disk_unittest.cc

// Copyright 2018 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/disks/disk.h"

#include <stdint.h>

#include <string>
#include <utility>
#include <vector>

#include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
#include "dbus/message.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {
namespace disks {
namespace {

const char kDevicePath[] = "/sys/device/path";
const char kDeviceFile[] = "/dev/sdb1";
const char kMountPath1[] = "/media/removable/UNTITLED";
const char kMountPath2[] = "/media/removable/second_mount_path";
const char kDriveModel[] = "DriveModel";
const char kIdLabel[] = "UNTITLED";
const char kIdUuid[] = "XXXX-YYYY";
const char kStorageDevicePath[] =
    "/sys/devices/pci0000:00/0000:00:14.0/usb2/2-8/2-8:1.0/host14/target14:0:0/"
    "14:0:0:0";
const char kProductId[] = "1234";
const char kProductName[] = "Product Name";
const char kVendorId[] = "0000";
const char kVendorName[] = "Vendor Name";
const char kFileSystemType[] = "exfat";
const int kBusNumber = 2;
const int kDeviceNumber = 3;
const uint64_t kDeviceSize = 16005464064;
const uint32_t kDeviceMediaType = static_cast<uint32_t>(ash::DeviceType::kSD);

// Appends a boolean entry to a dictionary of type "a{sv}"
void AppendBoolDictEntry(dbus::MessageWriter* array_writer,
                         const std::string& key,
                         bool value) {
  dbus::MessageWriter entry_writer(nullptr);
  array_writer->OpenDictEntry(&entry_writer);
  entry_writer.AppendString(key);
  entry_writer.AppendVariantOfBool(value);
  array_writer->CloseContainer(&entry_writer);
}

// Appends a string entry to a dictionary of type "a{sv}"
void AppendStringDictEntry(dbus::MessageWriter* array_writer,
                           const std::string& key,
                           const std::string& value) {
  dbus::MessageWriter entry_writer(nullptr);
  array_writer->OpenDictEntry(&entry_writer);
  entry_writer.AppendString(key);
  entry_writer.AppendVariantOfString(value);
  array_writer->CloseContainer(&entry_writer);
}

// Appends a uint64 entry to a dictionary of type "a{sv}"
void AppendUint64DictEntry(dbus::MessageWriter* array_writer,
                           const std::string& key,
                           uint64_t value) {
  dbus::MessageWriter entry_writer(nullptr);
  array_writer->OpenDictEntry(&entry_writer);
  entry_writer.AppendString(key);
  entry_writer.AppendVariantOfUint64(value);
  array_writer->CloseContainer(&entry_writer);
}

// Appends a uint32 entry to a dictionary of type "a{sv}"
void AppendUint32DictEntry(dbus::MessageWriter* array_writer,
                           const std::string& key,
                           uint64_t value) {
  dbus::MessageWriter entry_writer(nullptr);
  array_writer->OpenDictEntry(&entry_writer);
  entry_writer.AppendString(key);
  entry_writer.AppendVariantOfUint32(value);
  array_writer->CloseContainer(&entry_writer);
}

// Appends a Int32 entry to a dictionary of type "a{sv}"
void AppendInt32DictEntry(dbus::MessageWriter* array_writer,
                          const std::string& key,
                          int value) {
  dbus::MessageWriter entry_writer(nullptr);
  array_writer->OpenDictEntry(&entry_writer);
  entry_writer.AppendString(key);
  entry_writer.AppendVariantOfInt32(value);
  array_writer->CloseContainer(&entry_writer);
}

void AppendBasicProperties(dbus::MessageWriter* array_writer) {
  AppendStringDictEntry(array_writer, cros_disks::kDeviceFile, kDeviceFile);
  AppendStringDictEntry(array_writer, cros_disks::kDriveModel, kDriveModel);
  AppendStringDictEntry(array_writer, cros_disks::kIdLabel, kIdLabel);
  AppendStringDictEntry(array_writer, cros_disks::kIdUuid, kIdUuid);
  AppendStringDictEntry(array_writer, cros_disks::kStorageDevicePath,
                        kStorageDevicePath);
  AppendStringDictEntry(array_writer, cros_disks::kProductId, kProductId);
  AppendStringDictEntry(array_writer, cros_disks::kProductName, kProductName);
  AppendStringDictEntry(array_writer, cros_disks::kVendorId, kVendorId);
  AppendStringDictEntry(array_writer, cros_disks::kVendorName, kVendorName);
  AppendStringDictEntry(array_writer, cros_disks::kFileSystemType,
                        kFileSystemType);
  AppendInt32DictEntry(array_writer, cros_disks::kBusNumber, kBusNumber);
  AppendInt32DictEntry(array_writer, cros_disks::kDeviceNumber, kDeviceNumber);
  AppendUint64DictEntry(array_writer, cros_disks::kDeviceSize, kDeviceSize);
  AppendUint32DictEntry(array_writer, cros_disks::kDeviceMediaType,
                        kDeviceMediaType);
}

// Builds a dbus reponse with a common set of fields.
std::unique_ptr<dbus::Response> BuildBasicDbusResponse() {
  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
  dbus::MessageWriter writer(response.get());
  dbus::MessageWriter array_writer(nullptr);

  writer.OpenArray("{sv}", &array_writer);
  AppendBasicProperties(&array_writer);
  writer.CloseContainer(&array_writer);

  return response;
}

TEST(DiskTest, ConstructFromDiskInfo) {
  const char kBaseMountpath[] = "/base/mount/path";

  std::unique_ptr<dbus::Response> response = BuildBasicDbusResponse();
  DiskInfo disk_info(kDevicePath, response.get());
  Disk disk(disk_info, false /* write_disabled_by_policy */, kBaseMountpath);

  EXPECT_EQ(kDevicePath, disk.device_path());
  EXPECT_EQ(kDeviceFile, disk.file_path());
  EXPECT_EQ(kIdLabel, disk.device_label());
  EXPECT_EQ(kDriveModel, disk.drive_label());
  EXPECT_EQ(kVendorId, disk.vendor_id());
  EXPECT_EQ(kVendorName, disk.vendor_name());
  EXPECT_EQ(kProductId, disk.product_id());
  EXPECT_EQ(kProductName, disk.product_name());
  EXPECT_EQ(kIdUuid, disk.fs_uuid());
  EXPECT_EQ(kBusNumber, disk.bus_number());
  EXPECT_EQ(kDeviceNumber, disk.device_number());
  EXPECT_EQ(kDeviceSize, disk.total_size_in_bytes());
  EXPECT_EQ(DeviceType::kSD, disk.device_type());
  EXPECT_EQ(kStorageDevicePath, disk.storage_device_path());
  EXPECT_EQ(kBaseMountpath, disk.base_mount_path());
  EXPECT_FALSE(disk.is_parent());
  EXPECT_FALSE(disk.is_read_only());
  EXPECT_FALSE(disk.is_read_only_hardware());
  EXPECT_FALSE(disk.has_media());
  EXPECT_FALSE(disk.on_boot_device());
  EXPECT_FALSE(disk.on_removable_device());
  EXPECT_FALSE(disk.is_mounted());
  EXPECT_FALSE(disk.IsStatefulPartition());
  EXPECT_FALSE(disk.is_auto_mountable());
  EXPECT_TRUE(disk.is_first_mount());

  // Drives are hidden by default.
  EXPECT_TRUE(disk.is_hidden());
}

std::unique_ptr<Disk> BuildDiskWithProperty(const std::string& property,
                                            bool value) {
  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
  {
    dbus::MessageWriter writer(response.get());
    dbus::MessageWriter array_writer(nullptr);

    writer.OpenArray("{sv}", &array_writer);
    AppendBasicProperties(&array_writer);
    AppendBoolDictEntry(&array_writer, property, value);
    writer.CloseContainer(&array_writer);
  }
  DiskInfo disk_info(kDevicePath, response.get());
  return std::make_unique<Disk>(disk_info, false, "");
}

TEST(DiskTest, ConstructFromDiskInfo_BoolProperties) {
  {
    auto disk = BuildDiskWithProperty(cros_disks::kDeviceIsDrive, true);
    EXPECT_TRUE(disk->is_parent());
  }
  {
    auto disk = BuildDiskWithProperty(cros_disks::kDeviceIsReadOnly, true);
    EXPECT_TRUE(disk->is_read_only());
    EXPECT_TRUE(disk->is_read_only_hardware());
  }
  {
    auto disk =
        BuildDiskWithProperty(cros_disks::kDeviceIsMediaAvailable, true);
    EXPECT_TRUE(disk->has_media());
  }
  {
    auto disk = BuildDiskWithProperty(cros_disks::kDeviceIsOnBootDevice, true);
    EXPECT_TRUE(disk->on_boot_device());
  }
  {
    auto disk =
        BuildDiskWithProperty(cros_disks::kDeviceIsOnRemovableDevice, true);
    EXPECT_TRUE(disk->on_removable_device());
  }
  {
    auto disk = BuildDiskWithProperty(cros_disks::kIsAutoMountable, true);
    EXPECT_TRUE(disk->is_auto_mountable());
  }
}

TEST(DiskTest, ConstructFromDiskInfo_WriteDisabledByPolicy) {
  std::unique_ptr<dbus::Response> response = BuildBasicDbusResponse();
  DiskInfo disk_info(kDevicePath, response.get());
  Disk disk(disk_info, true /* write_disabled_by_policy */, "");

  EXPECT_TRUE(disk.is_read_only());
  EXPECT_FALSE(disk.is_read_only_hardware());
}

TEST(DiskTest, ConstructFromDiskInfo_Mounted) {
  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
  {
    dbus::MessageWriter writer(response.get());
    dbus::MessageWriter array_writer(nullptr);

    writer.OpenArray("{sv}", &array_writer);
    AppendBasicProperties(&array_writer);
    {
      std::vector<std::string> mounted_paths = {kMountPath1, kMountPath2};

      dbus::MessageWriter entry_writer(nullptr);
      array_writer.OpenDictEntry(&entry_writer);
      entry_writer.AppendString(cros_disks::kDeviceMountPaths);
      dbus::MessageWriter variant_writer(nullptr);
      entry_writer.OpenVariant("as", &variant_writer);
      variant_writer.AppendArrayOfStrings(mounted_paths);
      entry_writer.CloseContainer(&variant_writer);
      array_writer.CloseContainer(&entry_writer);
    }
    writer.CloseContainer(&array_writer);
  }

  DiskInfo disk_info(kDevicePath, response.get());
  Disk disk(disk_info, false, "");

  EXPECT_TRUE(disk.is_mounted());
  EXPECT_EQ(kMountPath1, disk.mount_path());
}

TEST(DiskTest, SetMountPath) {
  std::unique_ptr<dbus::Response> response = BuildBasicDbusResponse();
  DiskInfo disk_info(kDevicePath, response.get());
  Disk disk(disk_info, false /* write_disabled_by_policy */, "");

  EXPECT_EQ("", disk.mount_path());
  EXPECT_EQ("", disk.base_mount_path());
  EXPECT_FALSE(disk.is_mounted());

  disk.SetMountPath(kMountPath1);
  EXPECT_EQ(kMountPath1, disk.mount_path());
  EXPECT_EQ(kMountPath1, disk.base_mount_path());
  EXPECT_FALSE(disk.is_mounted());

  disk.set_mounted(true);
  EXPECT_TRUE(disk.is_mounted());
}

}  // namespace
}  // namespace disks
}  // namespace ash