chromium/components/device_signals/core/browser/mac/plist_settings_client_unittest.mm

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/device_signals/core/browser/mac/plist_settings_client.h"

#import <Foundation/Foundation.h>

#include <string>

#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "components/device_signals/test/test_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace device_signals {

class PlistSettingsClientTest : public testing::Test {
 protected:
  GetSettingsOptions CreateOption(const std::string& key_path, bool value) {
    GetSettingsOptions option;
    option.path = test_file_path_.value();
    option.key = key_path;
    option.get_value = value;
    return option;
  }

  SettingsItem CreateSettingItem(const std::string& key_path,
                                 PresenceValue value,
                                 const std::string& setting_value) {
    SettingsItem item;
    if (!setting_value.empty())
      item.setting_json_value = setting_value;
    return FinishSettingItemSetup(item, key_path, value);
  }

  SettingsItem CreateSettingItem(const std::string& key_path,
                                 PresenceValue value,
                                 const int setting_value) {
    SettingsItem item;
    item.setting_json_value = base::StringPrintf("%d", setting_value);
    return FinishSettingItemSetup(item, key_path, value);
  }

  SettingsItem CreateSettingItem(const std::string& key_path,
                                 PresenceValue value,
                                 const double setting_value) {
    SettingsItem item;
    item.setting_json_value = base::StringPrintf("%f", setting_value);
    return FinishSettingItemSetup(item, key_path, value);
  }

  SettingsItem FinishSettingItemSetup(SettingsItem item,
                                      const std::string& key_path,
                                      PresenceValue value) {
    item.path = test_file_path_.value();
    item.key = key_path;
    item.presence = value;
    return item;
  }

  PlistSettingsClient client_;
  base::FilePath test_file_path_;
  base::test::TaskEnvironment task_environment_;
  base::test::TestFuture<const std::vector<SettingsItem>&> future_;
};

// Tests an empty request to GetSettings. In this case we should have no setting
// items.
TEST_F(PlistSettingsClientTest, GetSettings_EmptyOptions) {
  client_.GetSettings(std::vector<GetSettingsOptions>(), future_.GetCallback());
  EXPECT_EQ(std::vector<SettingsItem>(), future_.Get());
}

// Tests when the request to GetSettings contains one invalid request. In this
// case we should have no setting items.
TEST_F(PlistSettingsClientTest, GetSettings_InvalidOptions) {
  test_file_path_ = test::GetMixArrayDictionaryPlistPath();

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption("", false));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem("", PresenceValue::kNotFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings from a plist that does not exist.
TEST_F(PlistSettingsClientTest, GetSettings_PlistDoesNotExist) {
  test_file_path_ =
      test::GetTestDataDir().AppendASCII("plist-doesnt-exist.plist");

  std::vector<GetSettingsOptions> options;
  std::string key_path = "Key1.SubKey1.SubSubKey1[0]";
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kNotFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings from an empty plist.
TEST_F(PlistSettingsClientTest, GetSettings_Plist_EmptyPlist) {
  test_file_path_ = test::GetEmptyPlistPath();

  std::vector<GetSettingsOptions> options;
  std::string key_path = "Key1.SubKey1";
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kNotFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings from a plist containing a mix of dictionaries
// and arrays with nested key paths that do not exist in the plist.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_MixOfArrayDictItems_InvalidKeyPath) {
  test_file_path_ = test::GetMixArrayDictionaryPlistPath();

  std::string key_path1 = "Key1.SubKey1.SubSubKey1[0][20]";
  std::string key_path2 = "Key1.SubKey1.SubSubKey5[1";
  std::string key_path3 = "Key1.Array]";
  std::string key_path4 = "Key1.Array[1000000][";
  std::string key_path5 = "Key1..";
  std::string key_path6 = "Key1.Array[a]";
  std::string key_path7 = "Key1.Array[";
  std::string key_path8 = "Key1.Array[1]]";
  std::string key_path9 = "Key1.Array[]";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path1, true));
  options.push_back(CreateOption(key_path2, false));
  options.push_back(CreateOption(key_path3, false));
  options.push_back(CreateOption(key_path4, true));
  options.push_back(CreateOption(key_path5, true));
  options.push_back(CreateOption(key_path6, false));
  options.push_back(CreateOption(key_path7, false));
  options.push_back(CreateOption(key_path8, true));
  options.push_back(CreateOption(key_path9, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path1, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path2, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path3, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path4, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path5, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path6, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path7, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path8, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path9, PresenceValue::kNotFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings from a plist containing a mix of dictionaries
// and arrays with a nested key path that exists in the plist.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_MixOfArrayDictItems_ValidKeyPath) {
  test_file_path_ = test::GetMixArrayDictionaryPlistPath();

  std::string key_path = "Key1.SubKey1.SubSubKey1[0][10]";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));
  options.push_back(CreateOption(key_path, false));

  std::vector<SettingsItem> items;
  items.push_back(
      CreateSettingItem(key_path, PresenceValue::kFound, "\"string10\""));
  items.push_back(CreateSettingItem(key_path, PresenceValue::kFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with an invalid setting path from a plist
// containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_InvalidKeyPath) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path1 = "Key1.InvalidKeyValue";
  std::string key_path2 = "Key1.InvalidKeyValue.SubKey1";
  std::string key_path3 = "Key1.SubKey1.SubKey2.SubKey3.SubKey4";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path1, true));
  options.push_back(CreateOption(key_path2, false));
  options.push_back(CreateOption(key_path3, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path1, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path2, PresenceValue::kNotFound, ""));
  items.push_back(CreateSettingItem(key_path3, PresenceValue::kNotFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with a path to a data type setting value from
// a plist containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_DataType) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path = "DataKeyValue";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kFound, ""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with a path to a float type setting value from
// a plist containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_FloatType) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path = "RealKeyValue";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kFound, 0.123));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with a path to a integer type setting value
// from a plist containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_IntegerType) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path = "Key1.SubKey1.IntegerKeyValue";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kFound, 100));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with a path to a string type setting value
// from a plist containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_StringType) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path = "Key1.SubKey1.StringKeyValue";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(
      CreateSettingItem(key_path, PresenceValue::kFound, "\"string\""));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

// Tests a request to GetSettings with a path to a boolean type setting value
// from a plist containing only dictionary elements.
TEST_F(PlistSettingsClientTest,
       GetSettings_Plist_OnlyDictionaryItems_BooleanType) {
  test_file_path_ = test::GetOnlyDictionaryPlistPath();

  std::string key_path = "Key1.BooleanKeyValue";

  std::vector<GetSettingsOptions> options;
  options.push_back(CreateOption(key_path, true));

  std::vector<SettingsItem> items;
  items.push_back(CreateSettingItem(key_path, PresenceValue::kFound, 1));

  client_.GetSettings(options, future_.GetCallback());
  EXPECT_EQ(items, future_.Get());
}

}  // namespace device_signals