blob: 9d5b0d655fc82f9938acdcdf08bc1798b4371d67 [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
18using buffet::unittests::CreateDictionaryValue;
19using buffet::unittests::ValueToString;
20
21namespace buffet {
22
23class StatePackageTestHelper {
24 public:
25 // Returns the state property definitions (types/constraints/etc).
26 static const ObjectSchema& GetTypes(const StatePackage& package) {
27 return package.types_;
28 }
29 // Returns the all state property values in this package.
30 static const native_types::Object& GetValues(const StatePackage& package) {
31 return package.values_;
32 }
33};
34
35namespace {
36std::unique_ptr<base::DictionaryValue> GetTestSchema() {
37 return CreateDictionaryValue(R"({
38 'light': 'boolean',
39 'color': 'string',
40 'direction':{'properties':{'azimuth':'number','altitude':{'maximum':90.0}}},
41 'iso': [50, 100, 200, 400]
42 })");
43}
44
45std::unique_ptr<base::DictionaryValue> GetTestValues() {
46 return CreateDictionaryValue(R"({
47 'light': true,
48 'color': 'white',
49 'direction': {'azimuth':57.2957795, 'altitude':89.9},
50 'iso': 200
51 })");
52}
53
54inline const ObjectSchema& GetTypes(const StatePackage& package) {
55 return StatePackageTestHelper::GetTypes(package);
56}
57// Returns the all state property values in this package.
58inline const native_types::Object& GetValues(const StatePackage& package) {
59 return StatePackageTestHelper::GetValues(package);
60}
61
62} // anonymous namespace
63
64class StatePackageTest : public ::testing::Test {
65 public:
66 void SetUp() override {
67 package_.reset(new StatePackage("test"));
68 ASSERT_TRUE(package_->AddSchemaFromJson(GetTestSchema().get(), nullptr));
69 ASSERT_TRUE(package_->AddValuesFromJson(GetTestValues().get(), nullptr));
70 }
71 void TearDown() override {
72 package_.reset();
73 }
74 std::unique_ptr<StatePackage> package_;
75};
76
77TEST(StatePackage, Empty) {
78 StatePackage package("test");
79 EXPECT_EQ("test", package.GetName());
80 EXPECT_TRUE(GetTypes(package).GetProps().empty());
81 EXPECT_TRUE(GetValues(package).empty());
82}
83
84TEST(StatePackage, AddSchemaFromJson_OnEmpty) {
85 StatePackage package("test");
86 ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
87 EXPECT_EQ(4, GetTypes(package).GetProps().size());
88 EXPECT_EQ(4, GetValues(package).size());
89 EXPECT_EQ("{'color':{'type':'string'},"
90 "'direction':{'properties':{"
91 "'altitude':{'maximum':90.0,'type':'number'},"
92 "'azimuth':{'type':'number'}},"
93 "'type':'object'},"
94 "'iso':{'enum':[50,100,200,400],'type':'integer'},"
95 "'light':{'type':'boolean'}}",
96 ValueToString(GetTypes(package).ToJson(true, nullptr).get()));
97 EXPECT_EQ("{'color':'','direction':{},'iso':0,'light':false}",
98 ValueToString(package.GetValuesAsJson(nullptr).get()));
99}
100
101TEST(StatePackage, AddValuesFromJson_OnEmpty) {
102 StatePackage package("test");
103 ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
104 ASSERT_TRUE(package.AddValuesFromJson(GetTestValues().get(), nullptr));
105 EXPECT_EQ(4, GetValues(package).size());
106 EXPECT_EQ("{'color':'white',"
107 "'direction':{'altitude':89.9,'azimuth':57.2957795},"
108 "'iso':200,"
109 "'light':true}",
110 ValueToString(package.GetValuesAsJson(nullptr).get()));
111}
112
113TEST_F(StatePackageTest, AddSchemaFromJson_AddMore) {
114 auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}");
115 ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
116 EXPECT_EQ(5, GetTypes(*package_).GetProps().size());
117 EXPECT_EQ(5, GetValues(*package_).size());
118 EXPECT_EQ("{'brightness':{'enum':['low','medium','high'],'type':'string'},"
119 "'color':{'type':'string'},"
120 "'direction':{'properties':{"
121 "'altitude':{'maximum':90.0,'type':'number'},"
122 "'azimuth':{'type':'number'}},"
123 "'type':'object'},"
124 "'iso':{'enum':[50,100,200,400],'type':'integer'},"
125 "'light':{'type':'boolean'}}",
126 ValueToString(GetTypes(*package_).ToJson(true, nullptr).get()));
127 EXPECT_EQ("{'brightness':'',"
128 "'color':'white',"
129 "'direction':{'altitude':89.9,'azimuth':57.2957795},"
130 "'iso':200,"
131 "'light':true}",
132 ValueToString(package_->GetValuesAsJson(nullptr).get()));
133}
134
135TEST_F(StatePackageTest, AddValuesFromJson_AddMore) {
136 auto dict = CreateDictionaryValue("{'brightness':['low', 'medium', 'high']}");
137 ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
138 dict = CreateDictionaryValue("{'brightness':'medium'}");
139 ASSERT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr));
140 EXPECT_EQ(5, GetValues(*package_).size());
141 EXPECT_EQ("{'brightness':'medium',"
142 "'color':'white',"
143 "'direction':{'altitude':89.9,'azimuth':57.2957795},"
144 "'iso':200,"
145 "'light':true}",
146 ValueToString(package_->GetValuesAsJson(nullptr).get()));
147}
148
149TEST_F(StatePackageTest, AddSchemaFromJson_Error_Redefined) {
150 auto dict = CreateDictionaryValue("{'color':['white', 'blue', 'red']}");
151 chromeos::ErrorPtr error;
152 EXPECT_FALSE(package_->AddSchemaFromJson(dict.get(), &error));
153 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
154 EXPECT_EQ(errors::state::kPropertyRedefinition, error->GetCode());
155 EXPECT_EQ("State property 'test.color' is already defined",
156 error->GetMessage());
157}
158
159TEST_F(StatePackageTest, AddValuesFromJson_Error_Undefined) {
160 auto dict = CreateDictionaryValue("{'brightness':'medium'}");
161 chromeos::ErrorPtr error;
162 EXPECT_FALSE(package_->AddValuesFromJson(dict.get(), &error));
163 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
164 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
165 EXPECT_EQ("State property 'test.brightness' is not defined",
166 error->GetMessage());
167}
168
169TEST_F(StatePackageTest, GetPropertyValue) {
170 chromeos::Any value = package_->GetPropertyValue("color", nullptr);
171 EXPECT_EQ("white", value.TryGet<std::string>());
172
173 value = package_->GetPropertyValue("light", nullptr);
174 EXPECT_TRUE(value.TryGet<bool>());
175
176 value = package_->GetPropertyValue("iso", nullptr);
177 EXPECT_EQ(200, value.TryGet<int>());
178
179 value = package_->GetPropertyValue("direction", nullptr);
Alex Vakulenko576c9792014-09-22 16:49:45 -0700180 auto direction = value.TryGet<chromeos::VariantDictionary>();
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700181 ASSERT_FALSE(direction.empty());
182 EXPECT_DOUBLE_EQ(89.9, direction["altitude"].TryGet<double>());
183 EXPECT_DOUBLE_EQ(57.2957795, direction["azimuth"].TryGet<double>());
184}
185
186TEST_F(StatePackageTest, GetPropertyValue_Unknown) {
187 chromeos::ErrorPtr error;
188 chromeos::Any value = package_->GetPropertyValue("volume", &error);
189 EXPECT_TRUE(value.IsEmpty());
190 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
191 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
192 EXPECT_EQ("State property 'test.volume' is not defined",
193 error->GetMessage());
194}
195
196TEST_F(StatePackageTest, SetPropertyValue_Simple) {
197 EXPECT_TRUE(package_->SetPropertyValue("color", std::string{"blue"},
198 nullptr));
199 chromeos::Any value = package_->GetPropertyValue("color", nullptr);
200 EXPECT_EQ("blue", value.TryGet<std::string>());
201
202 EXPECT_TRUE(package_->SetPropertyValue("light", bool{false}, nullptr));
203 value = package_->GetPropertyValue("light", nullptr);
204 EXPECT_FALSE(value.TryGet<bool>());
205
206 EXPECT_TRUE(package_->SetPropertyValue("iso", int{400}, nullptr));
207 value = package_->GetPropertyValue("iso", nullptr);
208 EXPECT_EQ(400, value.TryGet<int>());
209}
210
211TEST_F(StatePackageTest, SetPropertyValue_Object) {
Alex Vakulenko576c9792014-09-22 16:49:45 -0700212 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700213 {"altitude", double{45.0}},
214 {"azimuth", double{15.0}},
215 };
216 EXPECT_TRUE(package_->SetPropertyValue("direction", direction, nullptr));
217 EXPECT_EQ("{'color':'white',"
218 "'direction':{'altitude':45.0,'azimuth':15.0},"
219 "'iso':200,"
220 "'light':true}",
221 ValueToString(package_->GetValuesAsJson(nullptr).get()));
222}
223
224TEST_F(StatePackageTest, SetPropertyValue_Error_TypeMismatch) {
225 chromeos::ErrorPtr error;
226 ASSERT_FALSE(package_->SetPropertyValue("color", int{12}, &error));
227 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
228 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
229 EXPECT_EQ("Unable to convert value to type 'string'", error->GetMessage());
230 error.reset();
231
232 ASSERT_FALSE(package_->SetPropertyValue("iso", bool{false}, &error));
233 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
234 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
235 EXPECT_EQ("Unable to convert value to type 'integer'", error->GetMessage());
236}
237
238TEST_F(StatePackageTest, SetPropertyValue_Error_OutOfRange) {
239 chromeos::ErrorPtr error;
240 ASSERT_FALSE(package_->SetPropertyValue("iso", int{150}, &error));
241 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
242 EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode());
243 EXPECT_EQ("Value 150 is invalid. Expected one of [50,100,200,400]",
244 error->GetMessage());
245}
246
247TEST_F(StatePackageTest, SetPropertyValue_Error_Object_TypeMismatch) {
248 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700249 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700250 {"altitude", double{45.0}},
251 {"azimuth", int{15}},
252 };
253 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
254 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
255 EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode());
256 EXPECT_EQ("Invalid value for property 'azimuth'", error->GetMessage());
257 const chromeos::Error* inner = error->GetInnerError();
258 EXPECT_EQ(errors::commands::kDomain, inner->GetDomain());
259 EXPECT_EQ(errors::commands::kTypeMismatch, inner->GetCode());
260 EXPECT_EQ("Unable to convert value to type 'number'", inner->GetMessage());
261}
262
263TEST_F(StatePackageTest, SetPropertyValue_Error_Object_OutOfRange) {
264 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700265 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700266 {"altitude", double{100.0}},
267 {"azimuth", double{290.0}},
268 };
269 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
270 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
271 EXPECT_EQ(errors::commands::kInvalidPropValue, error->GetCode());
272 EXPECT_EQ("Invalid value for property 'altitude'", error->GetMessage());
273 const chromeos::Error* inner = error->GetInnerError();
274 EXPECT_EQ(errors::commands::kDomain, inner->GetDomain());
275 EXPECT_EQ(errors::commands::kOutOfRange, inner->GetCode());
276 EXPECT_EQ("Value 100 is out of range. It must not be greater than 90",
277 inner->GetMessage());
278}
279
280TEST_F(StatePackageTest, SetPropertyValue_Error_Object_UnknownProperty) {
281 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700282 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700283 {"altitude", double{10.0}},
284 {"azimuth", double{20.0}},
285 {"spin", double{30.0}},
286 };
287 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
288 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
289 EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode());
290 EXPECT_EQ("Unrecognized property 'spin'", error->GetMessage());
291}
292
293TEST_F(StatePackageTest, SetPropertyValue_Error_Object_MissingProperty) {
294 chromeos::ErrorPtr error;
Alex Vakulenko576c9792014-09-22 16:49:45 -0700295 chromeos::VariantDictionary direction{
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700296 {"altitude", double{10.0}},
297 };
298 ASSERT_FALSE(package_->SetPropertyValue("direction", direction, &error));
299 EXPECT_EQ(errors::commands::kDomain, error->GetDomain());
300 EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode());
301 EXPECT_EQ("Required parameter missing: azimuth", error->GetMessage());
302}
303
304TEST_F(StatePackageTest, SetPropertyValue_Error_Unknown) {
305 chromeos::ErrorPtr error;
306 ASSERT_FALSE(package_->SetPropertyValue("volume", int{100}, &error));
307 EXPECT_EQ(errors::state::kDomain, error->GetDomain());
308 EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
309 EXPECT_EQ("State property 'test.volume' is not defined",
310 error->GetMessage());
311}
312
313} // namespace buffet