blob: 179d2060d59dadd3136d3b292b85badb204e2d6b [file] [log] [blame]
Alex Vakulenko07216fe2014-09-19 15:31:09 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Deymof6cbe322014-11-10 19:55:35 -08005#include "buffet/states/state_package.h"
6
Alex Vakulenko07216fe2014-09-19 15:31:09 -07007#include <memory>
8#include <string>
9
10#include <base/values.h>
Alex Vakulenko576c9792014-09-22 16:49:45 -070011#include <chromeos/variant_dictionary.h>
Alex Vakulenko07216fe2014-09-19 15:31:09 -070012#include <gtest/gtest.h>
13
14#include "buffet/commands/schema_constants.h"
15#include "buffet/commands/unittest_utils.h"
16#include "buffet/states/error_codes.h"
Alex Vakulenko07216fe2014-09-19 15:31:09 -070017
Alex Vakulenko07216fe2014-09-19 15:31:09 -070018namespace buffet {
19
Vitaly Buka32005de2015-05-01 12:33:31 -070020using unittests::CreateDictionaryValue;
Vitaly Buka32005de2015-05-01 12:33:31 -070021
Alex Vakulenko07216fe2014-09-19 15:31:09 -070022class StatePackageTestHelper {
23 public:
24 // Returns the state property definitions (types/constraints/etc).
25 static const ObjectSchema& GetTypes(const StatePackage& package) {
26 return package.types_;
27 }
28 // Returns the all state property values in this package.
29 static const native_types::Object& GetValues(const StatePackage& package) {
30 return package.values_;
31 }
32};
33
34namespace {
35std::unique_ptr<base::DictionaryValue> GetTestSchema() {
36 return CreateDictionaryValue(R"({
37 'light': 'boolean',
38 'color': 'string',
39 'direction':{'properties':{'azimuth':'number','altitude':{'maximum':90.0}}},
40 'iso': [50, 100, 200, 400]
41 })");
42}
43
44std::unique_ptr<base::DictionaryValue> GetTestValues() {
45 return CreateDictionaryValue(R"({
46 'light': true,
47 'color': 'white',
48 'direction': {'azimuth':57.2957795, 'altitude':89.9},
49 'iso': 200
50 })");
51}
52
53inline const ObjectSchema& GetTypes(const StatePackage& package) {
54 return StatePackageTestHelper::GetTypes(package);
55}
56// Returns the all state property values in this package.
57inline const native_types::Object& GetValues(const StatePackage& package) {
58 return StatePackageTestHelper::GetValues(package);
59}
60
61} // anonymous namespace
62
63class StatePackageTest : public ::testing::Test {
64 public:
65 void SetUp() override {
66 package_.reset(new StatePackage("test"));
67 ASSERT_TRUE(package_->AddSchemaFromJson(GetTestSchema().get(), nullptr));
68 ASSERT_TRUE(package_->AddValuesFromJson(GetTestValues().get(), nullptr));
69 }
70 void TearDown() override {
71 package_.reset();
72 }
73 std::unique_ptr<StatePackage> package_;
74};
75
76TEST(StatePackage, Empty) {
77 StatePackage package("test");
78 EXPECT_EQ("test", package.GetName());
79 EXPECT_TRUE(GetTypes(package).GetProps().empty());
80 EXPECT_TRUE(GetValues(package).empty());
81}
82
83TEST(StatePackage, AddSchemaFromJson_OnEmpty) {
84 StatePackage package("test");
85 ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
86 EXPECT_EQ(4, GetTypes(package).GetProps().size());
87 EXPECT_EQ(4, GetValues(package).size());
Vitaly Buka7c82d292015-05-03 18:08:12 -070088
89 auto expected = R"({
90 'color': {
91 'type': 'string'
92 },
93 'direction': {
94 'additionalProperties': false,
95 'properties': {
96 'altitude': {
97 'maximum': 90.0,
98 'type': 'number'
99 },
100 'azimuth': {
101 'type': 'number'
102 }
103 },
104 'type': 'object'
105 },
106 'iso': {
107 'enum': [50, 100, 200, 400],
108 'type': 'integer'
109 },
110 'light': {
111 'type': 'boolean'
112 }
113 })";
114 EXPECT_JSON_EQ(expected, *GetTypes(package).ToJson(true, nullptr));
115
116 expected = R"({
117 'color': '',
118 'direction': {},
119 'iso': 0,
120 'light': false
121 })";
122 EXPECT_JSON_EQ(expected, *package.GetValuesAsJson(nullptr));
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700123}
124
125TEST(StatePackage, AddValuesFromJson_OnEmpty) {
126 StatePackage package("test");
127 ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
128 ASSERT_TRUE(package.AddValuesFromJson(GetTestValues().get(), nullptr));
129 EXPECT_EQ(4, GetValues(package).size());
Vitaly Buka7c82d292015-05-03 18:08:12 -0700130 auto expected = R"({
131 'color': 'white',
132 'direction': {
133 'altitude': 89.9,
134 'azimuth': 57.2957795
135 },
136 'iso': 200,
137 'light': true
138 })";
139 EXPECT_JSON_EQ(expected, *package.GetValuesAsJson(nullptr));
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700140}
141
142TEST_F(StatePackageTest, AddSchemaFromJson_AddMore) {
143 auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}");
144 ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
145 EXPECT_EQ(5, GetTypes(*package_).GetProps().size());
146 EXPECT_EQ(5, GetValues(*package_).size());
Vitaly Buka7c82d292015-05-03 18:08:12 -0700147 auto expected = R"({
148 'brightness': {
149 'enum': ['low', 'medium', 'high'],
150 'type': 'string'
151 },
152 'color': {
153 'type': 'string'
154 },
155 'direction': {
156 'additionalProperties': false,
157 'properties': {
158 'altitude': {
159 'maximum': 90.0,
160 'type': 'number'
161 },
162 'azimuth': {
163 'type': 'number'
164 }
165 },
166 'type': 'object'
167 },
168 'iso': {
169 'enum': [50, 100, 200, 400],
170 'type': 'integer'
171 },
172 'light': {
173 'type': 'boolean'
174 }
175 })";
176 EXPECT_JSON_EQ(expected, *GetTypes(*package_).ToJson(true, nullptr));
177
178 expected = R"({
179 'brightness': '',
180 'color': 'white',
181 'direction': {
182 'altitude': 89.9,
183 'azimuth': 57.2957795
184 },
185 'iso': 200,
186 'light': true
187 })";
188 EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700189}
190
191TEST_F(StatePackageTest, AddValuesFromJson_AddMore) {
192 auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}");
193 ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
194 dict = CreateDictionaryValue("{'brightness':'medium'}");
195 ASSERT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr));
196 EXPECT_EQ(5, GetValues(*package_).size());
Vitaly Buka7c82d292015-05-03 18:08:12 -0700197 auto expected = R"({
198 'brightness': 'medium',
199 'color': 'white',
200 'direction': {
201 'altitude': 89.9,
202 'azimuth': 57.2957795
203 },
204 'iso': 200,
205 'light': true
206 })";
207 EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700208}
209
210TEST_F(StatePackageTest, AddSchemaFromJson_Error_Redefined) {
211 auto dict = CreateDictionaryValue("{'color':['white', 'blue', 'red']}");
212 chromeos::ErrorPtr error;
213 EXPECT_FALSE(package_->AddSchemaFromJson(dict.get(), &error));
214 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
215 EXPECT_EQ(errors::state::kPropertyRedefinition, error->GetCode());
216 EXPECT_EQ("State property 'test.color' is already defined",
217 error->GetMessage());
218}
219
220TEST_F(StatePackageTest, AddValuesFromJson_Error_Undefined) {
221 auto dict = CreateDictionaryValue("{'brightness':'medium'}");
222 chromeos::ErrorPtr error;
223 EXPECT_FALSE(package_->AddValuesFromJson(dict.get(), &error));
224 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
225 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
226 EXPECT_EQ("State property 'test.brightness' is not defined",
227 error->GetMessage());
228}
229
230TEST_F(StatePackageTest, GetPropertyValue) {
231 chromeos::Any value = package_->GetPropertyValue("color", nullptr);
232 EXPECT_EQ("white", value.TryGet<std::string>());
233
234 value = package_->GetPropertyValue("light", nullptr);
235 EXPECT_TRUE(value.TryGet<bool>());
236
237 value = package_->GetPropertyValue("iso", nullptr);
238 EXPECT_EQ(200, value.TryGet<int>());
239
240 value = package_->GetPropertyValue("direction", nullptr);
Alex Vakulenko576c9792014-09-22 16:49:45 -0700241 auto direction = value.TryGet<chromeos::VariantDictionary>();
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700242 ASSERT_FALSE(direction.empty());
243 EXPECT_DOUBLE_EQ(89.9, direction["altitude"].TryGet<double>());
244 EXPECT_DOUBLE_EQ(57.2957795, direction["azimuth"].TryGet<double>());
245}
246
247TEST_F(StatePackageTest, GetPropertyValue_Unknown) {
248 chromeos::ErrorPtr error;
249 chromeos::Any value = package_->GetPropertyValue("volume", &error);
250 EXPECT_TRUE(value.IsEmpty());
251 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
252 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
253 EXPECT_EQ("State property 'test.volume' is not defined",
254 error->GetMessage());
255}
256
257TEST_F(StatePackageTest, SetPropertyValue_Simple) {
258 EXPECT_TRUE(package_->SetPropertyValue("color", std::string{"blue"},
259 nullptr));
260 chromeos::Any value = package_->GetPropertyValue("color", nullptr);
261 EXPECT_EQ("blue", value.TryGet<std::string>());
262
263 EXPECT_TRUE(package_->SetPropertyValue("light", bool{false}, nullptr));
264 value = package_->GetPropertyValue("light", nullptr);
265 EXPECT_FALSE(value.TryGet<bool>());
266
267 EXPECT_TRUE(package_->SetPropertyValue("iso", int{400}, nullptr));
268 value = package_->GetPropertyValue("iso", nullptr);
269 EXPECT_EQ(400, value.TryGet<int>());
270}
271
272TEST_F(StatePackageTest, SetPropertyValue_Object) {
Alex Vakulenko576c9792014-09-22 16:49:45 -0700273 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700274 {"altitude", double{45.0}},
275 {"azimuth", double{15.0}},
276 };
277 EXPECT_TRUE(package_->SetPropertyValue("direction", direction, nullptr));
Vitaly Buka7c82d292015-05-03 18:08:12 -0700278
279 auto expected = R"({
280 'color': 'white',
281 'direction': {
282 'altitude': 45.0,
283 'azimuth': 15.0
284 },
285 'iso': 200,
286 'light': true
287 })";
288 EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700289}
290
291TEST_F(StatePackageTest, SetPropertyValue_Error_TypeMismatch) {
292 chromeos::ErrorPtr error;
293 ASSERT_FALSE(package_->SetPropertyValue("color", int{12}, &error));
294 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
295 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
296 EXPECT_EQ("Unable to convert value to type 'string'", error->GetMessage());
297 error.reset();
298
299 ASSERT_FALSE(package_->SetPropertyValue("iso", bool{false}, &error));
300 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
301 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
302 EXPECT_EQ("Unable to convert value to type 'integer'", error->GetMessage());
303}
304
305TEST_F(StatePackageTest, SetPropertyValue_Error_OutOfRange) {
306 chromeos::ErrorPtr error;
307 ASSERT_FALSE(package_->SetPropertyValue("iso", int{150}, &error));
308 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
309 EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode());
310 EXPECT_EQ("Value 150 is invalid. Expected one of [50,100,200,400]",
311 error->GetMessage());
312}
313
314TEST_F(StatePackageTest, SetPropertyValue_Error_Object_TypeMismatch) {
315 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700316 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700317 {"altitude", double{45.0}},
318 {"azimuth", int{15}},
319 };
320 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
321 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
322 EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode());
323 EXPECT_EQ("Invalid value for property 'azimuth'", error->GetMessage());
324 const chromeos::Error* inner = error->GetInnerError();
325 EXPECT_EQ(errors::commands::kDomain, inner->GetDomain());
326 EXPECT_EQ(errors::commands::kTypeMismatch, inner->GetCode());
327 EXPECT_EQ("Unable to convert value to type 'number'", inner->GetMessage());
328}
329
330TEST_F(StatePackageTest, SetPropertyValue_Error_Object_OutOfRange) {
331 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700332 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700333 {"altitude", double{100.0}},
334 {"azimuth", double{290.0}},
335 };
336 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
337 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
338 EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode());
339 EXPECT_EQ("Invalid value for property 'altitude'", error->GetMessage());
340 const chromeos::Error* inner = error->GetInnerError();
341 EXPECT_EQ(errors::commands::kDomain, inner->GetDomain());
342 EXPECT_EQ(errors::commands::kOutOfRange, inner->GetCode());
343 EXPECT_EQ("Value 100 is out of range. It must not be greater than 90",
344 inner->GetMessage());
345}
346
347TEST_F(StatePackageTest, SetPropertyValue_Error_Object_UnknownProperty) {
348 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700349 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700350 {"altitude", double{10.0}},
351 {"azimuth", double{20.0}},
352 {"spin", double{30.0}},
353 };
354 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
355 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
356 EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode());
357 EXPECT_EQ("Unrecognized property 'spin'", error->GetMessage());
358}
359
360TEST_F(StatePackageTest, SetPropertyValue_Error_Object_MissingProperty) {
361 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700362 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700363 {"altitude", double{10.0}},
364 };
365 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
366 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
367 EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode());
368 EXPECT_EQ("Required parameter missing: azimuth", error->GetMessage());
369}
370
371TEST_F(StatePackageTest, SetPropertyValue_Error_Unknown) {
372 chromeos::ErrorPtr error;
373 ASSERT_FALSE(package_->SetPropertyValue("volume", int{100}, &error));
374 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
375 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
376 EXPECT_EQ("State property 'test.volume' is not defined",
377 error->GetMessage());
378}
379
380} // namespace buffet