libweave: Copy buffet_config* into config* I am going to split that into two different classes. No changes. This will help follow future changes. BUG=brillo:1257,brillo:1253 TEST=`FEATURES=test emerge-gizmo libweave buffet` Change-Id: Ie2e7b882186c959e35ff224c211325e1e6c14c94 Reviewed-on: https://chromium-review.googlesource.com/293897 Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Tested-by: Vitaly Buka <vitalybuka@chromium.org> Reviewed-by: Alex Vakulenko <avakulenko@chromium.org> Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/src/config.cc b/libweave/src/config.cc new file mode 100644 index 0000000..85c75fe --- /dev/null +++ b/libweave/src/config.cc
@@ -0,0 +1,295 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/src/config.h" + +#include <set> + +#include <base/files/file_util.h> +#include <base/logging.h> +#include <base/strings/string_number_conversions.h> +#include <weave/enum_to_string.h> + +#include "libweave/src/storage_impls.h" +#include "libweave/src/storage_interface.h" +#include "libweave/src/string_utils.h" + +namespace { + +bool IsValidAccessRole(const std::string& role) { + return role == "none" || role == "viewer" || role == "user"; +} + +bool StringToTimeDelta(const std::string& value, base::TimeDelta* delta) { + uint64_t ms{0}; + if (!base::StringToUint64(value, &ms)) + return false; + *delta = base::TimeDelta::FromMilliseconds(ms); + return true; +} + +} // namespace + +namespace weave { + +namespace config_keys { + +const char kClientId[] = "client_id"; +const char kClientSecret[] = "client_secret"; +const char kApiKey[] = "api_key"; +const char kOAuthURL[] = "oauth_url"; +const char kServiceURL[] = "service_url"; +const char kName[] = "name"; +const char kDescription[] = "description"; +const char kLocation[] = "location"; +const char kLocalAnonymousAccessRole[] = "local_anonymous_access_role"; +const char kLocalDiscoveryEnabled[] = "local_discovery_enabled"; +const char kLocalPairingEnabled[] = "local_pairing_enabled"; +const char kOemName[] = "oem_name"; +const char kModelName[] = "model_name"; +const char kModelId[] = "model_id"; +const char kPollingPeriodMs[] = "polling_period_ms"; +const char kBackupPollingPeriodMs[] = "backup_polling_period_ms"; +const char kRefreshToken[] = "refresh_token"; +const char kDeviceId[] = "device_id"; +const char kRobotAccount[] = "robot_account"; +const char kWifiAutoSetupEnabled[] = "wifi_auto_setup_enabled"; +const char kBleSetupEnabled[] = "ble_setup_enabled"; +const char kEmbeddedCode[] = "embedded_code"; +const char kPairingModes[] = "pairing_modes"; +const char kLastConfiguredSsid[] = "last_configured_ssid"; + +} // namespace config_keys + +Settings Config::CreateDefaultSettings() { + Settings result; + result.client_id = "58855907228.apps.googleusercontent.com"; + result.client_secret = "eHSAREAHrIqPsHBxCE9zPPBi"; + result.api_key = "AIzaSyDSq46gG-AxUnC3zoqD9COIPrjolFsMfMA"; + result.oauth_url = "https://accounts.google.com/o/oauth2/"; + result.service_url = "https://www.googleapis.com/clouddevices/v1/"; + result.name = "Developer device"; + result.local_anonymous_access_role = "viewer"; + result.local_discovery_enabled = true; + result.local_pairing_enabled = true; + result.oem_name = "Chromium"; + result.model_name = "Brillo"; + result.model_id = "AAAAA"; + result.polling_period = base::TimeDelta::FromSeconds(7); + result.backup_polling_period = base::TimeDelta::FromMinutes(30); + result.wifi_auto_setup_enabled = true; + result.ble_setup_enabled = false; + result.pairing_modes.emplace(PairingType::kPinCode); + return result; +} + +Config::Config(std::unique_ptr<StorageInterface> storage) + : storage_{std::move(storage)} {} + +Config::Config(const base::FilePath& state_path) + : Config{std::unique_ptr<StorageInterface>{new FileStorage{state_path}}} {} + +void Config::AddOnChangedCallback(const OnChangedCallback& callback) { + on_changed_.push_back(callback); + // Force to read current state. + callback.Run(settings_); +} + +const Settings& Config::GetSettings() const { + return settings_; +} + +void Config::Load(const base::FilePath& config_path) { + chromeos::KeyValueStore store; + if (base::PathExists(config_path)) { + CHECK(store.Load(config_path)) << "Unable to read or parse config file at" + << config_path.value(); + } + Load(store); +} + +void Config::Load(const chromeos::KeyValueStore& store) { + Transaction change{this}; + change.save_ = false; + + store.GetString(config_keys::kClientId, &settings_.client_id); + CHECK(!settings_.client_id.empty()); + + store.GetString(config_keys::kClientSecret, &settings_.client_secret); + CHECK(!settings_.client_secret.empty()); + + store.GetString(config_keys::kApiKey, &settings_.api_key); + CHECK(!settings_.api_key.empty()); + + store.GetString(config_keys::kOAuthURL, &settings_.oauth_url); + CHECK(!settings_.oauth_url.empty()); + + store.GetString(config_keys::kServiceURL, &settings_.service_url); + CHECK(!settings_.service_url.empty()); + + store.GetString(config_keys::kOemName, &settings_.oem_name); + CHECK(!settings_.oem_name.empty()); + + store.GetString(config_keys::kModelName, &settings_.model_name); + CHECK(!settings_.model_name.empty()); + + store.GetString(config_keys::kModelId, &settings_.model_id); + CHECK(!settings_.model_id.empty()); + + std::string polling_period_str; + if (store.GetString(config_keys::kPollingPeriodMs, &polling_period_str)) + CHECK(StringToTimeDelta(polling_period_str, &settings_.polling_period)); + + if (store.GetString(config_keys::kBackupPollingPeriodMs, &polling_period_str)) + CHECK(StringToTimeDelta(polling_period_str, + &settings_.backup_polling_period)); + + store.GetBoolean(config_keys::kWifiAutoSetupEnabled, + &settings_.wifi_auto_setup_enabled); + + store.GetBoolean(config_keys::kBleSetupEnabled, &settings_.ble_setup_enabled); + + store.GetString(config_keys::kEmbeddedCode, &settings_.embedded_code); + + std::string modes_str; + if (store.GetString(config_keys::kPairingModes, &modes_str)) { + std::set<PairingType> pairing_modes; + for (const std::string& mode : Split(modes_str, ",", true, true)) { + PairingType pairing_mode; + CHECK(StringToEnum(mode, &pairing_mode)); + pairing_modes.insert(pairing_mode); + } + settings_.pairing_modes = std::move(pairing_modes); + } + + // Empty name set by user or server is allowed, still we expect some + // meaningfull config value. + store.GetString(config_keys::kName, &settings_.name); + CHECK(!settings_.name.empty()); + + store.GetString(config_keys::kDescription, &settings_.description); + store.GetString(config_keys::kLocation, &settings_.location); + + store.GetString(config_keys::kLocalAnonymousAccessRole, + &settings_.local_anonymous_access_role); + CHECK(IsValidAccessRole(settings_.local_anonymous_access_role)) + << "Invalid role: " << settings_.local_anonymous_access_role; + + store.GetBoolean(config_keys::kLocalDiscoveryEnabled, + &settings_.local_discovery_enabled); + store.GetBoolean(config_keys::kLocalPairingEnabled, + &settings_.local_pairing_enabled); + + change.LoadState(); +} + +void Config::Transaction::LoadState() { + if (!config_->storage_) + return; + auto value = config_->storage_->Load(); + const base::DictionaryValue* dict = nullptr; + if (!value || !value->GetAsDictionary(&dict)) + return; + + std::string tmp; + bool tmp_bool{false}; + + if (dict->GetString(config_keys::kClientId, &tmp)) + set_client_id(tmp); + + if (dict->GetString(config_keys::kClientSecret, &tmp)) + set_client_secret(tmp); + + if (dict->GetString(config_keys::kApiKey, &tmp)) + set_api_key(tmp); + + if (dict->GetString(config_keys::kOAuthURL, &tmp)) + set_oauth_url(tmp); + + if (dict->GetString(config_keys::kServiceURL, &tmp)) + set_service_url(tmp); + + if (dict->GetString(config_keys::kName, &tmp)) + set_name(tmp); + + if (dict->GetString(config_keys::kDescription, &tmp)) + set_description(tmp); + + if (dict->GetString(config_keys::kLocation, &tmp)) + set_location(tmp); + + if (dict->GetString(config_keys::kLocalAnonymousAccessRole, &tmp)) + set_local_anonymous_access_role(tmp); + + if (dict->GetBoolean(config_keys::kLocalDiscoveryEnabled, &tmp_bool)) + set_local_discovery_enabled(tmp_bool); + + if (dict->GetBoolean(config_keys::kLocalPairingEnabled, &tmp_bool)) + set_local_pairing_enabled(tmp_bool); + + if (dict->GetString(config_keys::kRefreshToken, &tmp)) + set_refresh_token(tmp); + + if (dict->GetString(config_keys::kRobotAccount, &tmp)) + set_robot_account(tmp); + + if (dict->GetString(config_keys::kLastConfiguredSsid, &tmp)) + set_last_configured_ssid(tmp); + + if (dict->GetString(config_keys::kDeviceId, &tmp)) + set_device_id(tmp); +} + +bool Config::Save() { + if (!storage_) + return false; + base::DictionaryValue dict; + dict.SetString(config_keys::kClientId, settings_.client_id); + dict.SetString(config_keys::kClientSecret, settings_.client_secret); + dict.SetString(config_keys::kApiKey, settings_.api_key); + dict.SetString(config_keys::kOAuthURL, settings_.oauth_url); + dict.SetString(config_keys::kServiceURL, settings_.service_url); + dict.SetString(config_keys::kRefreshToken, settings_.refresh_token); + dict.SetString(config_keys::kDeviceId, settings_.device_id); + dict.SetString(config_keys::kRobotAccount, settings_.robot_account); + dict.SetString(config_keys::kLastConfiguredSsid, + settings_.last_configured_ssid); + dict.SetString(config_keys::kName, settings_.name); + dict.SetString(config_keys::kDescription, settings_.description); + dict.SetString(config_keys::kLocation, settings_.location); + dict.SetString(config_keys::kLocalAnonymousAccessRole, + settings_.local_anonymous_access_role); + dict.SetBoolean(config_keys::kLocalDiscoveryEnabled, + settings_.local_discovery_enabled); + dict.SetBoolean(config_keys::kLocalPairingEnabled, + settings_.local_pairing_enabled); + + return storage_->Save(dict); +} + +Config::Transaction::~Transaction() { + Commit(); +} + +bool Config::Transaction::set_local_anonymous_access_role( + const std::string& role) { + if (!IsValidAccessRole(role)) { + LOG(ERROR) << "Invalid role: " << role; + return false; + } + settings_->local_anonymous_access_role = role; + return true; +} + +void Config::Transaction::Commit() { + if (!config_) + return; + if (save_) + config_->Save(); + for (const auto& cb : config_->on_changed_) + cb.Run(*settings_); + config_ = nullptr; +} + +} // namespace weave
diff --git a/libweave/src/config.h b/libweave/src/config.h new file mode 100644 index 0000000..5871629 --- /dev/null +++ b/libweave/src/config.h
@@ -0,0 +1,154 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_SRC_CONFIG_H_ +#define LIBWEAVE_SRC_CONFIG_H_ + +#include <set> +#include <string> +#include <vector> + +#include <base/callback.h> +#include <base/files/file_path.h> +#include <weave/error.h> +#include <chromeos/key_value_store.h> +#include <weave/config.h> + +#include "libweave/src/privet/security_delegate.h" + +namespace weave { + +class StorageInterface; + +// Handles reading buffet config and state files. +class Config final : public Config { + public: + using OnChangedCallback = base::Callback<void(const Settings&)>; + ~Config() override = default; + + explicit Config(std::unique_ptr<StorageInterface> storage); + + explicit Config(const base::FilePath& state_path); + + // Config overrides. + void AddOnChangedCallback(const OnChangedCallback& callback) override; + const Settings& GetSettings() const override; + + void Load(const base::FilePath& config_path); + void Load(const chromeos::KeyValueStore& store); + + // Allows editing of config. Makes sure that callbacks were called and changes + // were saved. + // User can commit changes by calling Commit method or by destroying the + // object. + class Transaction final { + public: + explicit Transaction(Config* config) + : config_(config), settings_(&config->settings_) { + CHECK(config_); + } + + ~Transaction(); + + void set_client_id(const std::string& id) { settings_->client_id = id; } + void set_client_secret(const std::string& secret) { + settings_->client_secret = secret; + } + void set_api_key(const std::string& key) { settings_->api_key = key; } + void set_oauth_url(const std::string& url) { settings_->oauth_url = url; } + void set_service_url(const std::string& url) { + settings_->service_url = url; + } + void set_name(const std::string& name) { settings_->name = name; } + void set_description(const std::string& description) { + settings_->description = description; + } + void set_location(const std::string& location) { + settings_->location = location; + } + bool set_local_anonymous_access_role(const std::string& role); + void set_local_discovery_enabled(bool enabled) { + settings_->local_discovery_enabled = enabled; + } + void set_local_pairing_enabled(bool enabled) { + settings_->local_pairing_enabled = enabled; + } + void set_device_id(const std::string& id) { settings_->device_id = id; } + void set_refresh_token(const std::string& token) { + settings_->refresh_token = token; + } + void set_robot_account(const std::string& account) { + settings_->robot_account = account; + } + void set_last_configured_ssid(const std::string& ssid) { + settings_->last_configured_ssid = ssid; + } + + void Commit(); + + private: + friend class Config; + void LoadState(); + Config* config_; + Settings* settings_; + bool save_{true}; + }; + + const std::string& client_id() const { return settings_.client_id; } + const std::string& client_secret() const { return settings_.client_secret; } + const std::string& api_key() const { return settings_.api_key; } + const std::string& oauth_url() const { return settings_.oauth_url; } + const std::string& service_url() const { return settings_.service_url; } + const std::string& oem_name() const { return settings_.oem_name; } + const std::string& model_name() const { return settings_.model_name; } + const std::string& model_id() const { return settings_.model_id; } + base::TimeDelta polling_period() const { return settings_.polling_period; } + base::TimeDelta backup_polling_period() const { + return settings_.backup_polling_period; + } + + bool wifi_auto_setup_enabled() const { + return settings_.wifi_auto_setup_enabled; + } + bool ble_setup_enabled() const { return settings_.ble_setup_enabled; } + const std::set<PairingType>& pairing_modes() const { + return settings_.pairing_modes; + } + const std::string& embedded_code() const { return settings_.embedded_code; } + + const std::string& name() const { return settings_.name; } + const std::string& description() const { return settings_.description; } + const std::string& location() const { return settings_.location; } + const std::string& local_anonymous_access_role() const { + return settings_.local_anonymous_access_role; + } + bool local_pairing_enabled() const { return settings_.local_pairing_enabled; } + bool local_discovery_enabled() const { + return settings_.local_discovery_enabled; + } + + const std::string& device_id() const { return settings_.device_id; } + const std::string& refresh_token() const { return settings_.refresh_token; } + const std::string& robot_account() const { return settings_.robot_account; } + const std::string& last_configured_ssid() const { + return settings_.last_configured_ssid; + } + + private: + bool Save(); + static Settings CreateDefaultSettings(); + + Settings settings_ = CreateDefaultSettings(); + + // Serialization interface to save and load buffet state. + std::unique_ptr<StorageInterface> storage_; + + std::vector<OnChangedCallback> on_changed_; + + DISALLOW_COPY_AND_ASSIGN(Config); +}; + +} // namespace weave + +#endif // LIBWEAVE_SRC_CONFIG_H_
diff --git a/libweave/src/config_unittest.cc b/libweave/src/config_unittest.cc new file mode 100644 index 0000000..0ffad43 --- /dev/null +++ b/libweave/src/config_unittest.cc
@@ -0,0 +1,314 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/src/config.h" + +#include <set> + +#include <base/bind.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "libweave/src/commands/unittest_utils.h" +#include "libweave/src/storage_impls.h" + +using testing::_; + +namespace weave { + +class ConfigTest : public ::testing::Test { + protected: + void SetUp() override { + EXPECT_CALL(*this, OnConfigChanged(_)) + .Times(1); // Called from AddOnChangedCallback + + std::unique_ptr<StorageInterface> storage{new MemStorage}; + storage_ = storage.get(); + config_.reset(new Config{std::move(storage)}); + + config_->AddOnChangedCallback( + base::Bind(&ConfigTest::OnConfigChanged, base::Unretained(this))); + } + + MOCK_METHOD1(OnConfigChanged, void(const Settings&)); + + StorageInterface* storage_{nullptr}; + std::unique_ptr<Config> config_; + const Config default_{nullptr}; +}; + +TEST_F(ConfigTest, NoStorage) { + Config config{nullptr}; + Config::Transaction change{&config}; + change.Commit(); +} + +TEST_F(ConfigTest, Defaults) { + EXPECT_EQ("58855907228.apps.googleusercontent.com", config_->client_id()); + EXPECT_EQ("eHSAREAHrIqPsHBxCE9zPPBi", config_->client_secret()); + EXPECT_EQ("AIzaSyDSq46gG-AxUnC3zoqD9COIPrjolFsMfMA", config_->api_key()); + EXPECT_EQ("https://accounts.google.com/o/oauth2/", config_->oauth_url()); + EXPECT_EQ("https://www.googleapis.com/clouddevices/v1/", + config_->service_url()); + EXPECT_EQ("Chromium", config_->oem_name()); + EXPECT_EQ("Brillo", config_->model_name()); + EXPECT_EQ("AAAAA", config_->model_id()); + EXPECT_EQ(base::TimeDelta::FromSeconds(7), config_->polling_period()); + EXPECT_EQ(base::TimeDelta::FromMinutes(30), config_->backup_polling_period()); + EXPECT_TRUE(config_->wifi_auto_setup_enabled()); + EXPECT_FALSE(config_->ble_setup_enabled()); + EXPECT_EQ(std::set<PairingType>{PairingType::kPinCode}, + config_->pairing_modes()); + EXPECT_EQ("", config_->embedded_code()); + EXPECT_EQ("Developer device", config_->name()); + EXPECT_EQ("", config_->description()); + EXPECT_EQ("", config_->location()); + EXPECT_EQ("viewer", config_->local_anonymous_access_role()); + EXPECT_TRUE(config_->local_pairing_enabled()); + EXPECT_TRUE(config_->local_discovery_enabled()); + EXPECT_EQ("", config_->device_id()); + EXPECT_EQ("", config_->refresh_token()); + EXPECT_EQ("", config_->robot_account()); + EXPECT_EQ("", config_->last_configured_ssid()); +} + +TEST_F(ConfigTest, LoadConfig) { + chromeos::KeyValueStore config_store; + config_store.SetString("client_id", "conf_client_id"); + config_store.SetString("client_secret", "conf_client_secret"); + config_store.SetString("api_key", "conf_api_key"); + config_store.SetString("oauth_url", "conf_oauth_url"); + config_store.SetString("service_url", "conf_service_url"); + config_store.SetString("oem_name", "conf_oem_name"); + config_store.SetString("model_name", "conf_model_name"); + config_store.SetString("model_id", "ABCDE"); + config_store.SetString("polling_period_ms", "12345"); + config_store.SetString("backup_polling_period_ms", "6589"); + config_store.SetBoolean("wifi_auto_setup_enabled", false); + config_store.SetBoolean("ble_setup_enabled", true); + config_store.SetString("pairing_modes", + "pinCode,embeddedCode,ultrasound32,audible32"); + config_store.SetString("embedded_code", "1234"); + config_store.SetString("name", "conf_name"); + config_store.SetString("description", "conf_description"); + config_store.SetString("location", "conf_location"); + config_store.SetString("local_anonymous_access_role", "user"); + config_store.SetBoolean("local_pairing_enabled", false); + config_store.SetBoolean("local_discovery_enabled", false); + + // Following will be ignored. + config_store.SetString("device_kind", "conf_device_kind"); + config_store.SetString("device_id", "conf_device_id"); + config_store.SetString("refresh_token", "conf_refresh_token"); + config_store.SetString("robot_account", "conf_robot_account"); + config_store.SetString("last_configured_ssid", "conf_last_configured_ssid"); + + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); + config_->Load(config_store); + + EXPECT_EQ("conf_client_id", config_->client_id()); + EXPECT_EQ("conf_client_secret", config_->client_secret()); + EXPECT_EQ("conf_api_key", config_->api_key()); + EXPECT_EQ("conf_oauth_url", config_->oauth_url()); + EXPECT_EQ("conf_service_url", config_->service_url()); + EXPECT_EQ("conf_oem_name", config_->oem_name()); + EXPECT_EQ("conf_model_name", config_->model_name()); + EXPECT_EQ("ABCDE", config_->model_id()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(12345), + config_->polling_period()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(6589), + config_->backup_polling_period()); + EXPECT_FALSE(config_->wifi_auto_setup_enabled()); + EXPECT_TRUE(config_->ble_setup_enabled()); + std::set<PairingType> pairing_types{ + PairingType::kPinCode, PairingType::kEmbeddedCode, + PairingType::kUltrasound32, PairingType::kAudible32}; + EXPECT_EQ(pairing_types, config_->pairing_modes()); + EXPECT_EQ("1234", config_->embedded_code()); + EXPECT_EQ("conf_name", config_->name()); + EXPECT_EQ("conf_description", config_->description()); + EXPECT_EQ("conf_location", config_->location()); + EXPECT_EQ("user", config_->local_anonymous_access_role()); + EXPECT_FALSE(config_->local_pairing_enabled()); + EXPECT_FALSE(config_->local_discovery_enabled()); + + // Not from config. + EXPECT_EQ(default_.device_id(), config_->device_id()); + EXPECT_EQ(default_.refresh_token(), config_->refresh_token()); + EXPECT_EQ(default_.robot_account(), config_->robot_account()); + EXPECT_EQ(default_.last_configured_ssid(), config_->last_configured_ssid()); + + // Nothing should be saved yet. + EXPECT_JSON_EQ("{}", *storage_->Load()); + + Config::Transaction change{config_.get()}; + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); + change.Commit(); + + auto expected = R"({ + 'api_key': 'conf_api_key', + 'client_id': 'conf_client_id', + 'client_secret': 'conf_client_secret', + 'description': 'conf_description', + 'device_id': '', + 'local_anonymous_access_role': 'user', + 'local_discovery_enabled': false, + 'local_pairing_enabled': false, + 'location': 'conf_location', + 'name': 'conf_name', + 'oauth_url': 'conf_oauth_url', + 'refresh_token': '', + 'robot_account': '', + 'last_configured_ssid': '', + 'service_url': 'conf_service_url' + })"; + EXPECT_JSON_EQ(expected, *storage_->Load()); +} + +TEST_F(ConfigTest, LoadState) { + auto state = R"({ + 'api_key': 'state_api_key', + 'client_id': 'state_client_id', + 'client_secret': 'state_client_secret', + 'description': 'state_description', + 'device_id': 'state_device_id', + 'local_anonymous_access_role': 'user', + 'local_discovery_enabled': false, + 'local_pairing_enabled': false, + 'location': 'state_location', + 'name': 'state_name', + 'oauth_url': 'state_oauth_url', + 'refresh_token': 'state_refresh_token', + 'robot_account': 'state_robot_account', + 'last_configured_ssid': 'state_last_configured_ssid', + 'service_url': 'state_service_url' + })"; + storage_->Save(*unittests::CreateDictionaryValue(state)); + + chromeos::KeyValueStore config_store; + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); + config_->Load(config_store); + + // Clear storage. + storage_->Save(base::DictionaryValue()); + + EXPECT_EQ("state_client_id", config_->client_id()); + EXPECT_EQ("state_client_secret", config_->client_secret()); + EXPECT_EQ("state_api_key", config_->api_key()); + EXPECT_EQ("state_oauth_url", config_->oauth_url()); + EXPECT_EQ("state_service_url", config_->service_url()); + EXPECT_EQ(default_.oem_name(), config_->oem_name()); + EXPECT_EQ(default_.model_name(), config_->model_name()); + EXPECT_EQ(default_.model_id(), config_->model_id()); + EXPECT_EQ(default_.polling_period(), config_->polling_period()); + EXPECT_EQ(default_.backup_polling_period(), config_->backup_polling_period()); + EXPECT_EQ(default_.wifi_auto_setup_enabled(), + config_->wifi_auto_setup_enabled()); + EXPECT_EQ(default_.ble_setup_enabled(), config_->ble_setup_enabled()); + EXPECT_EQ(default_.pairing_modes(), config_->pairing_modes()); + EXPECT_EQ(default_.embedded_code(), config_->embedded_code()); + EXPECT_EQ("state_name", config_->name()); + EXPECT_EQ("state_description", config_->description()); + EXPECT_EQ("state_location", config_->location()); + EXPECT_EQ("user", config_->local_anonymous_access_role()); + EXPECT_FALSE(config_->local_pairing_enabled()); + EXPECT_FALSE(config_->local_discovery_enabled()); + EXPECT_EQ("state_device_id", config_->device_id()); + EXPECT_EQ("state_refresh_token", config_->refresh_token()); + EXPECT_EQ("state_robot_account", config_->robot_account()); + EXPECT_EQ("state_last_configured_ssid", config_->last_configured_ssid()); + + // Nothing should be saved yet. + EXPECT_JSON_EQ("{}", *storage_->Load()); + + Config::Transaction change{config_.get()}; + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); + change.Commit(); + + EXPECT_JSON_EQ(state, *storage_->Load()); +} + +TEST_F(ConfigTest, Setters) { + Config::Transaction change{config_.get()}; + + change.set_client_id("set_client_id"); + EXPECT_EQ("set_client_id", config_->client_id()); + + change.set_client_secret("set_client_secret"); + EXPECT_EQ("set_client_secret", config_->client_secret()); + + change.set_api_key("set_api_key"); + EXPECT_EQ("set_api_key", config_->api_key()); + + change.set_oauth_url("set_oauth_url"); + EXPECT_EQ("set_oauth_url", config_->oauth_url()); + + change.set_service_url("set_service_url"); + EXPECT_EQ("set_service_url", config_->service_url()); + + change.set_name("set_name"); + EXPECT_EQ("set_name", config_->name()); + + change.set_description("set_description"); + EXPECT_EQ("set_description", config_->description()); + + change.set_location("set_location"); + EXPECT_EQ("set_location", config_->location()); + + change.set_local_anonymous_access_role("viewer"); + EXPECT_EQ("viewer", config_->local_anonymous_access_role()); + + change.set_local_anonymous_access_role("none"); + EXPECT_EQ("none", config_->local_anonymous_access_role()); + + change.set_local_anonymous_access_role("user"); + EXPECT_EQ("user", config_->local_anonymous_access_role()); + + change.set_local_discovery_enabled(false); + EXPECT_FALSE(config_->local_discovery_enabled()); + + change.set_local_pairing_enabled(false); + EXPECT_FALSE(config_->local_pairing_enabled()); + + change.set_local_discovery_enabled(true); + EXPECT_TRUE(config_->local_discovery_enabled()); + + change.set_local_pairing_enabled(true); + EXPECT_TRUE(config_->local_pairing_enabled()); + + change.set_device_id("set_id"); + EXPECT_EQ("set_id", config_->device_id()); + + change.set_refresh_token("set_token"); + EXPECT_EQ("set_token", config_->refresh_token()); + + change.set_robot_account("set_account"); + EXPECT_EQ("set_account", config_->robot_account()); + + change.set_last_configured_ssid("set_last_configured_ssid"); + EXPECT_EQ("set_last_configured_ssid", config_->last_configured_ssid()); + + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); + change.Commit(); + + auto expected = R"({ + 'api_key': 'set_api_key', + 'client_id': 'set_client_id', + 'client_secret': 'set_client_secret', + 'description': 'set_description', + 'device_id': 'set_id', + 'local_anonymous_access_role': 'user', + 'local_discovery_enabled': true, + 'local_pairing_enabled': true, + 'location': 'set_location', + 'name': 'set_name', + 'oauth_url': 'set_oauth_url', + 'refresh_token': 'set_token', + 'robot_account': 'set_account', + 'last_configured_ssid': 'set_last_configured_ssid', + 'service_url': 'set_service_url' + })"; + EXPECT_JSON_EQ(expected, *storage_->Load()); +} +} // namespace weave