chromium/chromeos/components/onc/onc_test_utils.cc

// Copyright 2012 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/components/onc/onc_test_utils.h"

#include <utility>

#include "base/check.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "components/onc/onc_constants.h"

namespace chromeos::onc::test_utils {

namespace {

bool GetTestDataPath(const std::string& filename, base::FilePath* result_path) {
  base::ScopedAllowBlockingForTesting allow_io;

  base::FilePath path;
  if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path)) {
    LOG(FATAL) << "Failed to get the path to root for " << filename;
  }
  path = path.Append(FILE_PATH_LITERAL("chromeos"));
  path = path.Append(FILE_PATH_LITERAL("components"));
  path = path.Append(FILE_PATH_LITERAL("test"));
  path = path.Append(FILE_PATH_LITERAL("data"));
  path = path.Append(FILE_PATH_LITERAL("onc"));
  path = path.Append(FILE_PATH_LITERAL(filename));
  if (!base::PathExists(path)) {  // We don't want to create this.
    LOG(FATAL) << "The file doesn't exist: " << path;
  }

  *result_path = path;
  return true;
}

}  // namespace

std::string ReadTestData(const std::string& filename) {
  base::ScopedAllowBlockingForTesting allow_io;
  base::FilePath path;
  if (!GetTestDataPath(filename, &path)) {
    return "";
  }
  std::string result;
  base::ReadFileToString(path, &result);
  return result;
}

namespace {

base::Value ReadTestJson(const std::string& filename) {
  base::FilePath path;
  if (!GetTestDataPath(filename, &path)) {
    LOG(FATAL) << "Unable to get test file path for: " << filename;
  }
  JSONFileValueDeserializer deserializer(
      path,
      base::JSON_PARSE_CHROMIUM_EXTENSIONS | base::JSON_ALLOW_TRAILING_COMMAS);
  std::string error_message;
  std::unique_ptr<base::Value> result =
      deserializer.Deserialize(nullptr, &error_message);
  CHECK(result != nullptr) << "Couldn't json-deserialize file: " << filename
                           << ": " << error_message;
  return std::move(*result);
}

}  // namespace

base::Value::Dict ReadTestDictionary(const std::string& filename) {
  base::Value content = ReadTestJson(filename);
  CHECK(content.is_dict())
      << "File '" << filename
      << "' does not contain a dictionary as expected, but type "
      << content.type();
  return std::move(content.GetDict());
}

base::Value::List ReadTestList(const std::string& filename) {
  base::Value content = ReadTestJson(filename);
  CHECK(content.is_list()) << "File '" << filename
                           << "' does not contain a list as expected, but type "
                           << content.type();
  return std::move(content.GetList());
}

::testing::AssertionResult Equals(const base::Value::Dict* expected,
                                  const base::Value::Dict* actual) {
  CHECK(expected != nullptr);
  if (actual == nullptr) {
    return ::testing::AssertionFailure() << "Actual value pointer is nullptr";
  }

  if (*expected == *actual) {
    return ::testing::AssertionSuccess() << "Values are equal";
  }

  return ::testing::AssertionFailure() << "Values are unequal.\n"
                                       << "Expected value:\n"
                                       << *expected << "Actual value:\n"
                                       << *actual;
}

namespace {

base::Value::List ConvertToBaseValueList(
    const std::vector<std::string>& string_vector) {
  base::Value::List result;
  for (const std::string& str : string_vector) {
    result.Append(str);
  }
  return result;
}

}  // namespace

TestToplevelApnData::TestToplevelApnData(
    std::optional<std::string> id,
    std::optional<std::string> access_point_name,
    std::optional<std::string> ip_type,
    std::optional<std::vector<std::string>> apn_types,
    std::optional<std::vector<std::string>> admin_apn_list_ids,
    std::optional<std::vector<std::string>> psim_admin_assigned_apn_ids,
    std::optional<std::vector<std::string>> admin_assigned_apn_ids)
    : id(id),
      access_point_name(access_point_name),
      ip_type(ip_type),
      apn_types(apn_types),
      admin_apn_list_ids(admin_apn_list_ids),
      psim_admin_assigned_apn_ids(psim_admin_assigned_apn_ids),
      admin_assigned_apn_ids(admin_assigned_apn_ids) {}

TestToplevelApnData::~TestToplevelApnData() = default;

TestToplevelApnData::TestToplevelApnData(const TestToplevelApnData& other)
    : id(other.id),
      access_point_name(other.access_point_name),
      ip_type(other.ip_type),
      apn_types(other.apn_types),
      admin_apn_list_ids(other.admin_apn_list_ids),
      psim_admin_assigned_apn_ids(other.psim_admin_assigned_apn_ids),
      admin_assigned_apn_ids(other.admin_assigned_apn_ids) {}

// Helper function to build an individual APN dictionary
base::Value::Dict BuildApnDict(TestToplevelApnData apn_data) {
  base::Value::Dict apn_dict;

  auto maybe_set_string = [&](const char* key,
                              const std::optional<std::string>& value) {
    if (value.has_value()) {
      apn_dict.Set(key, *value);
    }
  };

  maybe_set_string(::onc::cellular_apn::kId, apn_data.id);
  maybe_set_string(::onc::cellular_apn::kAccessPointName,
                   apn_data.access_point_name);
  maybe_set_string(::onc::cellular_apn::kIpType, apn_data.ip_type);

  if (apn_data.apn_types) {
    apn_dict.Set(::onc::cellular_apn::kApnTypes,
                 ConvertToBaseValueList(apn_data.apn_types.value()));
  }

  return apn_dict;
}

const std::string GenerateTopLevelWithCellularWithAPNAsJSON(
    TestToplevelApnData apn_data) {
  base::Value::Dict top_level_dict =
      test_utils::ReadTestDictionary("toplevel_cellular_no_apn.onc");

  // Locate the Cellular config within the top-level dictionary
  base::Value::List* network_configs =
      top_level_dict.FindList(::onc::toplevel_config::kNetworkConfigurations);
  DCHECK(network_configs);
  base::Value::Dict* cellular_network_config_dict =
      network_configs->front().GetIfDict();
  DCHECK(cellular_network_config_dict);
  base::Value::Dict* cellular_dict =
      cellular_network_config_dict->FindDict(::onc::network_config::kCellular);
  DCHECK(cellular_dict);
  cellular_dict->Set(::onc::cellular::kAPN, BuildApnDict(apn_data));
  if (apn_data.admin_assigned_apn_ids.has_value()) {
    cellular_dict->Set(
        ::onc::cellular::kAdminAssignedAPNIds,
        ConvertToBaseValueList(apn_data.admin_assigned_apn_ids.value()));
  }

  if (apn_data.admin_apn_list_ids.has_value()) {
    base::Value::List admin_apn_list;

    for (const std::string& apn_id : apn_data.admin_apn_list_ids.value()) {
      // Use BuildApnDict with the apn_id and optional values as null
      TestToplevelApnData admin_apn_data;
      admin_apn_data.id = apn_id;
      admin_apn_data.access_point_name = "test-access-point-name";
      admin_apn_list.Append(BuildApnDict(admin_apn_data));
    }
    top_level_dict.Set(::onc::toplevel_config::kAdminAPNList,
                       std::move(admin_apn_list));
  }

  if (apn_data.psim_admin_assigned_apn_ids.has_value()) {
    // Locate the Global network config within the top-level dictionary
    base::Value::Dict* global_network_config = top_level_dict.FindDict(
        ::onc::toplevel_config::kGlobalNetworkConfiguration);
    DCHECK(global_network_config);
    global_network_config->Set(
        ::onc::global_network_config::kPSIMAdminAssignedAPNIds,
        ConvertToBaseValueList(apn_data.psim_admin_assigned_apn_ids.value()));
  }

  // Serialize to JSON string
  std::string json_output;
  if (!base::JSONWriter::WriteWithOptions(
          top_level_dict, base::JSONWriter::OPTIONS_PRETTY_PRINT,
          &json_output)) {
    LOG(ERROR) << "JSON serialization failed";
  }
  return json_output;
}

}  // namespace chromeos::onc::test_utils