blob: b35e16a3373d3d508eb7bb6c3a284ee6f42981f5 [file] [log] [blame]
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08001// Copyright 2015 The Weave 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 Vakulenkoba981152015-12-05 13:58:22 -08005#include "src/component_manager_impl.h"
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08006
7#include <map>
8
9#include <gtest/gtest.h>
10#include <weave/test/unittest_utils.h>
11
12#include "src/bind_lambda.h"
13#include "src/commands/schema_constants.h"
Alex Vakulenkoba981152015-12-05 13:58:22 -080014#include "src/mock_component_manager.h"
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080015
16namespace weave {
17
18using test::CreateDictionaryValue;
19
20namespace {
21
22bool HasTrait(const base::DictionaryValue& comp, const std::string& trait) {
23 const base::ListValue* list = nullptr;
24 if (!comp.GetList("traits", &list))
25 return false;
26 for (const base::Value* item : *list) {
27 std::string value;
28 if (item->GetAsString(&value) && value == trait)
29 return true;
30 }
31 return false;
32}
33
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080034// Creates sample trait/component trees:
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -080035// {
36// "traits": {
37// "t1": {},
38// "t2": {},
39// "t3": {},
40// "t4": {},
41// "t5": {},
42// "t6": {},
43// },
44// "components": {
45// "comp1": {
46// "traits": [ "t1" ],
47// "components": {
48// "comp2": [
49// { "traits": [ "t2" ] },
50// {
51// "traits": [ "t3" ],
52// "components": {
53// "comp3": {
54// "traits": [ "t4" ],
55// "components": {
56// "comp4": {
57// "traits": [ "t5", "t6" ]
58// }
59// }
60// }
61// }
62// }
63// ],
64// }
65// }
66// }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080067// }
68void CreateTestComponentTree(ComponentManager* manager) {
69 const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
70 auto json = CreateDictionaryValue(kTraits);
71 ASSERT_TRUE(manager->LoadTraits(*json, nullptr));
72 EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr));
73 EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t2"},
74 nullptr));
75 EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t3"},
76 nullptr));
77 EXPECT_TRUE(manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"},
78 nullptr));
79 EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4",
80 {"t5", "t6"}, nullptr));
81}
82
83// Test clock class to record predefined time intervals.
84// Implementation from base/test/simple_test_clock.{h|cc}
85class SimpleTestClock : public base::Clock {
86 public:
87 base::Time Now() override { return now_; }
88
89 // Advances the clock by |delta|.
90 void Advance(base::TimeDelta delta) { now_ += delta; }
91
92 // Sets the clock to the given time.
93 void SetNow(base::Time now) { now_ = now; }
94
95 private:
96 base::Time now_;
97};
98
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080099} // anonymous namespace
100
101TEST(ComponentManager, Empty) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800102 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800103 EXPECT_TRUE(manager.GetTraits().empty());
104 EXPECT_TRUE(manager.GetComponents().empty());
105}
106
107TEST(ComponentManager, LoadTraits) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800108 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800109 const char kTraits[] = R"({
110 "trait1": {
111 "commands": {
112 "command1": {
113 "minimalRole": "user",
114 "parameters": {"height": {"type": "integer"}}
115 }
116 },
117 "state": {
118 "property1": {"type": "boolean"}
119 }
120 },
121 "trait2": {
122 "state": {
123 "property2": {"type": "string"}
124 }
125 }
126 })";
127 auto json = CreateDictionaryValue(kTraits);
128 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
129 EXPECT_JSON_EQ(kTraits, manager.GetTraits());
130 EXPECT_TRUE(manager.GetComponents().empty());
131}
132
133TEST(ComponentManager, LoadTraitsDuplicateIdentical) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800134 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800135 const char kTraits1[] = R"({
136 "trait1": {
137 "commands": {
138 "command1": {
139 "minimalRole": "user",
140 "parameters": {"height": {"type": "integer"}}
141 }
142 },
143 "state": {
144 "property1": {"type": "boolean"}
145 }
146 },
147 "trait2": {
148 "state": {
149 "property2": {"type": "string"}
150 }
151 }
152 })";
153 auto json = CreateDictionaryValue(kTraits1);
154 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
155 const char kTraits2[] = R"({
156 "trait1": {
157 "commands": {
158 "command1": {
159 "minimalRole": "user",
160 "parameters": {"height": {"type": "integer"}}
161 }
162 },
163 "state": {
164 "property1": {"type": "boolean"}
165 }
166 },
167 "trait3": {
168 "state": {
169 "property3": {"type": "string"}
170 }
171 }
172 })";
173 json = CreateDictionaryValue(kTraits2);
174 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
175 const char kExpected[] = R"({
176 "trait1": {
177 "commands": {
178 "command1": {
179 "minimalRole": "user",
180 "parameters": {"height": {"type": "integer"}}
181 }
182 },
183 "state": {
184 "property1": {"type": "boolean"}
185 }
186 },
187 "trait2": {
188 "state": {
189 "property2": {"type": "string"}
190 }
191 },
192 "trait3": {
193 "state": {
194 "property3": {"type": "string"}
195 }
196 }
197 })";
198 EXPECT_JSON_EQ(kExpected, manager.GetTraits());
199}
200
201TEST(ComponentManager, LoadTraitsDuplicateOverride) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800202 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800203 const char kTraits1[] = R"({
204 "trait1": {
205 "commands": {
206 "command1": {
207 "minimalRole": "user",
208 "parameters": {"height": {"type": "integer"}}
209 }
210 },
211 "state": {
212 "property1": {"type": "boolean"}
213 }
214 },
215 "trait2": {
216 "state": {
217 "property2": {"type": "string"}
218 }
219 }
220 })";
221 auto json = CreateDictionaryValue(kTraits1);
222 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
223 const char kTraits2[] = R"({
224 "trait1": {
225 "commands": {
226 "command1": {
227 "minimalRole": "user",
228 "parameters": {"height": {"type": "string"}}
229 }
230 },
231 "state": {
232 "property1": {"type": "boolean"}
233 }
234 },
235 "trait3": {
236 "state": {
237 "property3": {"type": "string"}
238 }
239 }
240 })";
241 json = CreateDictionaryValue(kTraits2);
242 EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
243}
244
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800245TEST(ComponentManager, AddTraitDefChangedCallback) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800246 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800247 int count = 0;
248 int count2 = 0;
249 manager.AddTraitDefChangedCallback(base::Bind([&count]() { count++; }));
250 manager.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; }));
251 EXPECT_EQ(1, count);
252 EXPECT_EQ(1, count2);
253 // New definitions.
254 const char kTraits1[] = R"({
255 "trait1": {
256 "state": {
257 "property1": {"type": "boolean"}
258 }
259 },
260 "trait2": {
261 "state": {
262 "property2": {"type": "string"}
263 }
264 }
265 })";
266 auto json = CreateDictionaryValue(kTraits1);
267 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
268 EXPECT_EQ(2, count);
269 // Duplicate definition, shouldn't call the callback.
270 const char kTraits2[] = R"({
271 "trait1": {
272 "state": {
273 "property1": {"type": "boolean"}
274 }
275 }
276 })";
277 json = CreateDictionaryValue(kTraits2);
278 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
279 EXPECT_EQ(2, count);
280 // New definition, should call the callback now.
281 const char kTraits3[] = R"({
282 "trait3": {
283 "state": {
284 "property3": {"type": "string"}
285 }
286 }
287 })";
288 json = CreateDictionaryValue(kTraits3);
289 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
290 EXPECT_EQ(3, count);
291 // Wrong definition, shouldn't call the callback.
292 const char kTraits4[] = R"({
293 "trait4": "foo"
294 })";
295 json = CreateDictionaryValue(kTraits4);
296 EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
297 EXPECT_EQ(3, count);
298 // Make sure both callbacks were called the same number of times.
299 EXPECT_EQ(count2, count);
300}
301
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800302TEST(ComponentManager, LoadTraitsNotAnObject) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800303 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800304 const char kTraits1[] = R"({"trait1": 0})";
305 auto json = CreateDictionaryValue(kTraits1);
306 ErrorPtr error;
307 EXPECT_FALSE(manager.LoadTraits(*json, &error));
308 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
309}
310
311TEST(ComponentManager, FindTraitDefinition) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800312 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800313 const char kTraits[] = R"({
314 "trait1": {
315 "commands": {
316 "command1": {
317 "minimalRole": "user",
318 "parameters": {"height": {"type": "integer"}}
319 }
320 },
321 "state": {
322 "property1": {"type": "boolean"}
323 }
324 },
325 "trait2": {
326 "state": {
327 "property2": {"type": "string"}
328 }
329 }
330 })";
331 auto json = CreateDictionaryValue(kTraits);
332 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
333
334 const base::DictionaryValue* trait = manager.FindTraitDefinition("trait1");
335 ASSERT_NE(nullptr, trait);
336 const char kExpected1[] = R"({
337 "commands": {
338 "command1": {
339 "minimalRole": "user",
340 "parameters": {"height": {"type": "integer"}}
341 }
342 },
343 "state": {
344 "property1": {"type": "boolean"}
345 }
346 })";
347 EXPECT_JSON_EQ(kExpected1, *trait);
348
349 trait = manager.FindTraitDefinition("trait2");
350 ASSERT_NE(nullptr, trait);
351 const char kExpected2[] = R"({
352 "state": {
353 "property2": {"type": "string"}
354 }
355 })";
356 EXPECT_JSON_EQ(kExpected2, *trait);
357
358 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3"));
359}
360
361TEST(ComponentManager, FindCommandDefinition) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800362 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800363 const char kTraits[] = R"({
364 "trait1": {
365 "commands": {
366 "command1": {
367 "minimalRole": "user",
368 "parameters": {"height": {"type": "integer"}}
369 }
370 }
371 },
372 "trait2": {
373 "commands": {
374 "command1": {
375 "minimalRole": "manager"
376 },
377 "command2": {
378 "minimalRole": "owner"
379 }
380 }
381 }
382 })";
383 auto json = CreateDictionaryValue(kTraits);
384 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
385
386 const auto* cmd_def = manager.FindCommandDefinition("trait1.command1");
387 ASSERT_NE(nullptr, cmd_def);
388 const char kExpected1[] = R"({
389 "minimalRole": "user",
390 "parameters": {"height": {"type": "integer"}}
391 })";
392 EXPECT_JSON_EQ(kExpected1, *cmd_def);
393
394 cmd_def = manager.FindCommandDefinition("trait2.command1");
395 ASSERT_NE(nullptr, cmd_def);
396 const char kExpected2[] = R"({
397 "minimalRole": "manager"
398 })";
399 EXPECT_JSON_EQ(kExpected2, *cmd_def);
400
401 cmd_def = manager.FindCommandDefinition("trait2.command2");
402 ASSERT_NE(nullptr, cmd_def);
403 const char kExpected3[] = R"({
404 "minimalRole": "owner"
405 })";
406 EXPECT_JSON_EQ(kExpected3, *cmd_def);
407
408 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command2"));
409 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3.command1"));
410 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait"));
411 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command1.parameters"));
412}
413
414TEST(ComponentManager, GetMinimalRole) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800415 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800416 const char kTraits[] = R"({
417 "trait1": {
418 "commands": {
419 "command1": { "minimalRole": "user" },
420 "command2": { "minimalRole": "viewer" }
421 }
422 },
423 "trait2": {
424 "commands": {
425 "command1": { "minimalRole": "manager" },
426 "command2": { "minimalRole": "owner" }
427 }
428 }
429 })";
430 auto json = CreateDictionaryValue(kTraits);
431 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
432
433 UserRole role;
434 ASSERT_TRUE(manager.GetMinimalRole("trait1.command1", &role, nullptr));
435 EXPECT_EQ(UserRole::kUser, role);
436
437 ASSERT_TRUE(manager.GetMinimalRole("trait1.command2", &role, nullptr));
438 EXPECT_EQ(UserRole::kViewer, role);
439
440 ASSERT_TRUE(manager.GetMinimalRole("trait2.command1", &role, nullptr));
441 EXPECT_EQ(UserRole::kManager, role);
442
443 ASSERT_TRUE(manager.GetMinimalRole("trait2.command2", &role, nullptr));
444 EXPECT_EQ(UserRole::kOwner, role);
445
446 EXPECT_FALSE(manager.GetMinimalRole("trait1.command3", &role, nullptr));
447}
448
449TEST(ComponentManager, AddComponent) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800450 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800451 const char kTraits[] = R"({"trait1": {}, "trait2": {}, "trait3": {}})";
452 auto json = CreateDictionaryValue(kTraits);
453 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
454 EXPECT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
455 EXPECT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
456 const char kExpected[] = R"({
457 "comp1": {
458 "traits": ["trait1", "trait2"]
459 },
460 "comp2": {
461 "traits": ["trait3"]
462 }
463 })";
464 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
465
466 // 'trait4' is undefined, so can't add a component referring to it.
467 EXPECT_FALSE(manager.AddComponent("", "comp3", {"trait4"}, nullptr));
468}
469
470TEST(ComponentManager, AddSubComponent) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800471 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800472 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
473 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
474 EXPECT_TRUE(manager.AddComponent("comp1", "comp3", {}, nullptr));
475 EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
476 const char kExpected[] = R"({
477 "comp1": {
478 "traits": [],
479 "components": {
480 "comp2": {
481 "traits": [],
482 "components": {
483 "comp4": {
484 "traits": []
485 }
486 }
487 },
488 "comp3": {
489 "traits": []
490 }
491 }
492 }
493 })";
494 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
495}
496
497TEST(ComponentManager, AddComponentArrayItem) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800498 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800499 const char kTraits[] = R"({"foo": {}, "bar": {}})";
500 auto json = CreateDictionaryValue(kTraits);
501 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
502
503 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
504 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"foo"},
505 nullptr));
506 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"bar"},
507 nullptr));
508 EXPECT_TRUE(manager.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr));
509 EXPECT_TRUE(manager.AddComponent("comp1.comp2[1].comp3", "comp4", {},
510 nullptr));
511 const char kExpected[] = R"({
512 "comp1": {
513 "traits": [],
514 "components": {
515 "comp2": [
516 {
517 "traits": ["foo"]
518 },
519 {
520 "traits": ["bar"],
521 "components": {
522 "comp3": {
523 "traits": [],
524 "components": {
525 "comp4": {
526 "traits": []
527 }
528 }
529 }
530 }
531 }
532 ]
533 }
534 }
535 })";
536 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
537}
538
539TEST(ComponentManager, AddComponentExist) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800540 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800541 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
542 EXPECT_FALSE(manager.AddComponent("", "comp1", {}, nullptr));
543 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
544 EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
545}
546
547TEST(ComponentManager, AddComponentDoesNotExist) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800548 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800549 EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
550}
551
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800552TEST(ComponentManager, AddComponentTreeChangedCallback) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800553 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800554 int count = 0;
555 int count2 = 0;
556 manager.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; }));
557 manager.AddComponentTreeChangedCallback(
558 base::Bind([&count2]() { count2++; }));
559 EXPECT_EQ(1, count);
560 EXPECT_EQ(1, count2);
561 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
562 EXPECT_EQ(2, count);
563 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
564 EXPECT_EQ(3, count);
565 EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
566 EXPECT_EQ(4, count);
567 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
568 EXPECT_EQ(5, count);
569 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
570 EXPECT_EQ(6, count);
571 // Make sure both callbacks were called the same number of times.
572 EXPECT_EQ(count2, count);
573}
574
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800575TEST(ComponentManager, FindComponent) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800576 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800577 CreateTestComponentTree(&manager);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800578
579 const base::DictionaryValue* comp = manager.FindComponent("comp1", nullptr);
580 ASSERT_NE(nullptr, comp);
581 EXPECT_TRUE(HasTrait(*comp, "t1"));
582
583 comp = manager.FindComponent("comp1.comp2[0]", nullptr);
584 ASSERT_NE(nullptr, comp);
585 EXPECT_TRUE(HasTrait(*comp, "t2"));
586
587 comp = manager.FindComponent("comp1.comp2[1]", nullptr);
588 ASSERT_NE(nullptr, comp);
589 EXPECT_TRUE(HasTrait(*comp, "t3"));
590
591 comp = manager.FindComponent("comp1.comp2[1].comp3", nullptr);
592 ASSERT_NE(nullptr, comp);
593 EXPECT_TRUE(HasTrait(*comp, "t4"));
594
595 comp = manager.FindComponent("comp1.comp2[1].comp3.comp4", nullptr);
596 ASSERT_NE(nullptr, comp);
597 EXPECT_TRUE(HasTrait(*comp, "t5"));
598
599 // Some whitespaces don't hurt.
600 comp = manager.FindComponent(" comp1 . comp2 [ \t 1 ] . comp3.comp4 ",
601 nullptr);
602 EXPECT_NE(nullptr, comp);
603
604 // Now check some failure cases.
605 ErrorPtr error;
606 EXPECT_EQ(nullptr, manager.FindComponent("", &error));
607 EXPECT_NE(nullptr, error.get());
608 // 'comp2' doesn't exist:
609 EXPECT_EQ(nullptr, manager.FindComponent("comp2", nullptr));
610 // 'comp1.comp2' is an array, not a component:
611 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2", nullptr));
612 // 'comp1.comp2[3]' doesn't exist:
613 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[3]", nullptr));
614 // Empty component names:
615 EXPECT_EQ(nullptr, manager.FindComponent(".comp2[1]", nullptr));
616 EXPECT_EQ(nullptr, manager.FindComponent("comp1.[1]", nullptr));
617 // Invalid array indices:
618 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[s]", nullptr));
619 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[-2]", nullptr));
620 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1e1]", nullptr));
621 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1", nullptr));
622}
623
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800624TEST(ComponentManager, ParseCommandInstance) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800625 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800626 const char kTraits[] = R"({
627 "trait1": {
628 "commands": {
629 "command1": { "minimalRole": "user" },
630 "command2": { "minimalRole": "viewer" }
631 }
632 },
633 "trait2": {
634 "commands": {
635 "command1": { "minimalRole": "manager" },
636 "command2": { "minimalRole": "owner" }
637 }
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800638 },
639 "trait3": {
640 "commands": {
641 "command1": { "minimalRole": "manager" },
642 "command2": { "minimalRole": "owner" }
643 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800644 }
645 })";
646 auto traits = CreateDictionaryValue(kTraits);
647 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
648 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
649 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
650
651 std::string id;
652 const char kCommand1[] = R"({
653 "name": "trait1.command1",
Alex Vakulenkob736c982015-12-05 14:09:27 -0800654 "id": "1234-12345",
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800655 "component": "comp1",
656 "parameters": {}
657 })";
658 auto command1 = CreateDictionaryValue(kCommand1);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800659 EXPECT_NE(nullptr,
660 manager.ParseCommandInstance(*command1, Command::Origin::kLocal,
661 UserRole::kUser, &id, nullptr).get());
Alex Vakulenkob736c982015-12-05 14:09:27 -0800662 EXPECT_EQ("1234-12345", id);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800663 // Not enough access rights
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800664 EXPECT_EQ(nullptr,
665 manager.ParseCommandInstance(*command1, Command::Origin::kLocal,
666 UserRole::kViewer, &id, nullptr).get());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800667
668 const char kCommand2[] = R"({
669 "name": "trait1.command3",
670 "component": "comp1",
671 "parameters": {}
672 })";
673 auto command2 = CreateDictionaryValue(kCommand2);
674 // trait1.command3 doesn't exist
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800675 EXPECT_EQ(nullptr,
676 manager.ParseCommandInstance(*command2, Command::Origin::kLocal,
677 UserRole::kOwner, &id, nullptr).get());
Alex Vakulenkob736c982015-12-05 14:09:27 -0800678 EXPECT_TRUE(id.empty());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800679
680 const char kCommand3[] = R"({
681 "name": "trait2.command1",
682 "component": "comp1",
683 "parameters": {}
684 })";
685 auto command3 = CreateDictionaryValue(kCommand3);
686 // Component comp1 doesn't have trait2.
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800687 EXPECT_EQ(nullptr,
688 manager.ParseCommandInstance(*command3, Command::Origin::kLocal,
689 UserRole::kOwner, &id, nullptr).get());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800690
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800691 // No component specified, find the suitable component
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800692 const char kCommand4[] = R"({
693 "name": "trait1.command1",
694 "parameters": {}
695 })";
696 auto command4 = CreateDictionaryValue(kCommand4);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800697 auto command_instance = manager.ParseCommandInstance(
698 *command4, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
699 EXPECT_NE(nullptr, command_instance.get());
700 EXPECT_EQ("comp1", command_instance->GetComponent());
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800701
702 const char kCommand5[] = R"({
703 "name": "trait2.command1",
704 "parameters": {}
705 })";
706 auto command5 = CreateDictionaryValue(kCommand5);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800707 command_instance = manager.ParseCommandInstance(
708 *command5, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
709 EXPECT_NE(nullptr, command_instance.get());
710 EXPECT_EQ("comp2", command_instance->GetComponent());
711
712 // Cannot route the command, no component with 'trait3'.
713 const char kCommand6[] = R"({
714 "name": "trait3.command1",
715 "parameters": {}
716 })";
717 auto command6 = CreateDictionaryValue(kCommand6);
718 EXPECT_EQ(nullptr,
719 manager.ParseCommandInstance(*command6, Command::Origin::kLocal,
720 UserRole::kOwner, &id, nullptr).get());
721}
722
723TEST(ComponentManager, AddCommand) {
724 ComponentManagerImpl manager;
725 const char kTraits[] = R"({
726 "trait1": {
727 "commands": {
728 "command1": { "minimalRole": "user" }
729 }
730 }
731 })";
732 auto traits = CreateDictionaryValue(kTraits);
733 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
734 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
735
736 std::string id;
737 const char kCommand[] = R"({
738 "name": "trait1.command1",
739 "id": "1234-12345",
740 "component": "comp1",
741 "parameters": {}
742 })";
743 auto command = CreateDictionaryValue(kCommand);
744 auto command_instance = manager.ParseCommandInstance(
745 *command, Command::Origin::kLocal, UserRole::kUser, &id, nullptr);
746 ASSERT_NE(nullptr, command_instance.get());
747 manager.AddCommand(std::move(command_instance));
748 const auto* queued_command = manager.FindCommand(id);
749 ASSERT_NE(nullptr, queued_command);
750 EXPECT_EQ("trait1.command1", queued_command->GetName());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800751}
752
753TEST(ComponentManager, AddCommandHandler) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800754 ComponentManagerImpl manager;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800755 const char kTraits[] = R"({
756 "trait1": {
757 "commands": {
758 "command1": { "minimalRole": "user" }
759 }
760 },
761 "trait2": {
762 "commands": {
763 "command2": { "minimalRole": "user" }
764 }
765 }
766 })";
767 auto traits = CreateDictionaryValue(kTraits);
768 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
769 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
770 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
771
772 std::string last_tags;
773 auto handler = [&last_tags](int tag, const std::weak_ptr<Command>& command) {
774 if (!last_tags.empty())
775 last_tags += ',';
776 last_tags += std::to_string(tag);
777 };
778
779 manager.AddCommandHandler("comp1", "trait1.command1", base::Bind(handler, 1));
780 manager.AddCommandHandler("comp2", "trait1.command1", base::Bind(handler, 2));
781 manager.AddCommandHandler("comp2", "trait2.command2", base::Bind(handler, 3));
782 EXPECT_TRUE(last_tags.empty());
783
784 const char kCommand1[] = R"({
785 "name": "trait1.command1",
786 "component": "comp1"
787 })";
788 auto command1 = CreateDictionaryValue(kCommand1);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800789 auto command_instance = manager.ParseCommandInstance(
790 *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
791 ASSERT_NE(nullptr, command_instance.get());
792 manager.AddCommand(std::move(command_instance));
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800793 EXPECT_EQ("1", last_tags);
794 last_tags.clear();
795
796 const char kCommand2[] = R"({
797 "name": "trait1.command1",
798 "component": "comp2"
799 })";
800 auto command2 = CreateDictionaryValue(kCommand2);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800801 command_instance = manager.ParseCommandInstance(
802 *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
803 ASSERT_NE(nullptr, command_instance.get());
804 manager.AddCommand(std::move(command_instance));
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800805 EXPECT_EQ("2", last_tags);
806 last_tags.clear();
807
808 const char kCommand3[] = R"({
809 "name": "trait2.command2",
810 "component": "comp2",
811 "parameters": {}
812 })";
813 auto command3 = CreateDictionaryValue(kCommand3);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800814 command_instance = manager.ParseCommandInstance(
815 *command3, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
816 ASSERT_NE(nullptr, command_instance.get());
817 manager.AddCommand(std::move(command_instance));
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800818 EXPECT_EQ("3", last_tags);
819 last_tags.clear();
820}
821
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800822TEST(ComponentManager, SetStateProperties) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800823 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800824 CreateTestComponentTree(&manager);
825
826 const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
827 auto state1 = CreateDictionaryValue(kState1);
828 ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr));
829 const char kExpected1[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800830 "comp1": {
831 "traits": [ "t1" ],
832 "state": {"t1": {"p1": 0, "p2": "foo"}},
833 "components": {
834 "comp2": [
835 {
836 "traits": [ "t2" ]
837 },
838 {
839 "traits": [ "t3" ],
840 "components": {
841 "comp3": {
842 "traits": [ "t4" ],
843 "components": {
844 "comp4": {
845 "traits": [ "t5", "t6" ]
846 }
847 }
848 }
849 }
850 }
851 ]
852 }
853 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800854 })";
855 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
856
857 const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
858 auto state2 = CreateDictionaryValue(kState2);
859 ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr));
860
861 const char kExpected2[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800862 "comp1": {
863 "traits": [ "t1" ],
864 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
865 "components": {
866 "comp2": [
867 {
868 "traits": [ "t2" ]
869 },
870 {
871 "traits": [ "t3" ],
872 "components": {
873 "comp3": {
874 "traits": [ "t4" ],
875 "components": {
876 "comp4": {
877 "traits": [ "t5", "t6" ]
878 }
879 }
880 }
881 }
882 }
883 ]
884 }
885 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800886 })";
887 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
888
889 const char kState3[] = R"({"t5": {"p1": 1}})";
890 auto state3 = CreateDictionaryValue(kState3);
891 ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
892 nullptr));
893
894 const char kExpected3[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800895 "comp1": {
896 "traits": [ "t1" ],
897 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
898 "components": {
899 "comp2": [
900 {
901 "traits": [ "t2" ]
902 },
903 {
904 "traits": [ "t3" ],
905 "components": {
906 "comp3": {
907 "traits": [ "t4" ],
908 "components": {
909 "comp4": {
910 "traits": [ "t5", "t6" ],
911 "state": { "t5": { "p1": 1 } }
912 }
913 }
914 }
915 }
916 }
917 ]
918 }
919 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800920 })";
921 EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
922}
923
924TEST(ComponentManager, SetStatePropertiesFromJson) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800925 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800926 CreateTestComponentTree(&manager);
927
928 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
929 "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})",
930 nullptr));
931
932 const char kExpected[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800933 "comp1": {
934 "traits": [ "t1" ],
935 "components": {
936 "comp2": [
937 {
938 "traits": [ "t2" ]
939 },
940 {
941 "traits": [ "t3" ],
942 "components": {
943 "comp3": {
944 "traits": [ "t4" ],
945 "components": {
946 "comp4": {
947 "traits": [ "t5", "t6" ],
948 "state": {
949 "t5": { "p1": 3 },
950 "t6": { "p2": 5 }
951 }
952 }
953 }
954 }
955 }
956 }
957 ]
958 }
959 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800960 })";
961 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
962}
963
964TEST(ComponentManager, SetGetStateProperty) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800965 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800966 const char kTraits[] = R"({
967 "trait1": {
968 "state": {
969 "prop1": { "type": "string" },
970 "prop2": { "type": "integer" }
971 }
972 },
973 "trait2": {
974 "state": {
975 "prop3": { "type": "string" },
976 "prop4": { "type": "string" }
977 }
978 }
979 })";
980 auto traits = CreateDictionaryValue(kTraits);
981 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
982 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
983
984 base::StringValue p1("foo");
985 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
986
987 const char kExpected1[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800988 "comp1": {
989 "traits": [ "trait1", "trait2" ],
990 "state": {
991 "trait1": { "prop1": "foo" }
992 }
993 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800994 })";
995 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
996
997 base::FundamentalValue p2(2);
998 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
999
1000 const char kExpected2[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001001 "comp1": {
1002 "traits": [ "trait1", "trait2" ],
1003 "state": {
1004 "trait1": { "prop1": "foo" },
1005 "trait2": { "prop3": 2 }
1006 }
1007 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001008 })";
1009 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
1010 // Just the package name without property:
1011 EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr));
1012
1013 const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1",
1014 nullptr);
1015 ASSERT_NE(nullptr, value);
1016 EXPECT_TRUE(p1.Equals(value));
1017 value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr);
1018 ASSERT_NE(nullptr, value);
1019 EXPECT_TRUE(p2.Equals(value));
1020
1021 // Non-existing property:
1022 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr));
1023 // Non-existing component
1024 EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr));
1025 // Just the package name without property:
1026 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr));
1027}
1028
1029TEST(ComponentManager, AddStateChangedCallback) {
1030 SimpleTestClock clock;
Alex Vakulenkoba981152015-12-05 13:58:22 -08001031 ComponentManagerImpl manager{&clock};
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001032 const char kTraits[] = R"({
1033 "trait1": {
1034 "state": {
1035 "prop1": { "type": "string" },
1036 "prop2": { "type": "string" }
1037 }
1038 }
1039 })";
1040 auto traits = CreateDictionaryValue(kTraits);
1041 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1042 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
1043
1044 int count = 0;
1045 int count2 = 0;
1046 manager.AddStateChangedCallback(base::Bind([&count]() { count++; }));
1047 manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
1048 EXPECT_EQ(1, count);
1049 EXPECT_EQ(1, count2);
1050 EXPECT_EQ(0, manager.GetLastStateChangeId());
1051
1052 base::StringValue p1("foo");
1053 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
1054 EXPECT_EQ(2, count);
1055 EXPECT_EQ(2, count2);
1056 EXPECT_EQ(1, manager.GetLastStateChangeId());
1057
1058 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
1059 EXPECT_EQ(3, count);
1060 EXPECT_EQ(3, count2);
1061 EXPECT_EQ(2, manager.GetLastStateChangeId());
1062
1063 // Fail - no component.
1064 ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
1065 EXPECT_EQ(3, count);
1066 EXPECT_EQ(3, count2);
1067 EXPECT_EQ(2, manager.GetLastStateChangeId());
1068}
1069
1070TEST(ComponentManager, ComponentStateUpdates) {
1071 SimpleTestClock clock;
Alex Vakulenkoba981152015-12-05 13:58:22 -08001072 ComponentManagerImpl manager{&clock};
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001073 const char kTraits[] = R"({
1074 "trait1": {
1075 "state": {
1076 "prop1": { "type": "string" },
1077 "prop2": { "type": "string" }
1078 }
1079 },
1080 "trait2": {
1081 "state": {
1082 "prop3": { "type": "string" },
1083 "prop4": { "type": "string" }
1084 }
1085 }
1086 })";
1087 auto traits = CreateDictionaryValue(kTraits);
1088 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1089 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1090 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
1091
1092 std::vector<ComponentManager::UpdateID> updates1;
1093 auto callback1 = [&updates1](ComponentManager::UpdateID id) {
1094 updates1.push_back(id);
1095 };
1096 // State change queue is empty, callback should be called immediately.
1097 auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1));
1098 ASSERT_EQ(1u, updates1.size());
1099 EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front());
1100 updates1.clear();
1101
1102 base::StringValue foo("foo");
1103 base::Time time1 = base::Time::Now();
1104 clock.SetNow(time1);
1105 // These three updates should be grouped into two separate state change queue
1106 // items, since they all happen at the same time, but for two different
1107 // components.
1108 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
1109 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
1110 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
1111
1112 std::vector<ComponentManager::UpdateID> updates2;
1113 auto callback2 = [&updates2](ComponentManager::UpdateID id) {
1114 updates2.push_back(id);
1115 };
1116 // State change queue is not empty, so callback will be called later.
1117 auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2));
1118 EXPECT_TRUE(updates2.empty());
1119
1120 base::StringValue bar("bar");
1121 base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
1122 clock.SetNow(time2);
1123 // Two more update events (as above) but at |time2|.
1124 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
1125 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
1126 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
1127
1128 auto snapshot = manager.GetAndClearRecordedStateChanges();
1129 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id);
1130 ASSERT_EQ(4u, snapshot.state_changes.size());
1131
1132 EXPECT_EQ("comp1", snapshot.state_changes[0].component);
1133 EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
1134 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
1135 *snapshot.state_changes[0].changed_properties);
1136
1137 EXPECT_EQ("comp2", snapshot.state_changes[1].component);
1138 EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
1139 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
1140 *snapshot.state_changes[1].changed_properties);
1141
1142 EXPECT_EQ("comp1", snapshot.state_changes[2].component);
1143 EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
1144 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
1145 *snapshot.state_changes[2].changed_properties);
1146
1147 EXPECT_EQ("comp2", snapshot.state_changes[3].component);
1148 EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
1149 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
1150 *snapshot.state_changes[3].changed_properties);
1151
1152 // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
1153 auto snapshot2 = manager.GetAndClearRecordedStateChanges();
1154 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id);
1155 EXPECT_TRUE(snapshot2.state_changes.empty());
1156
1157 // Now indicate that we have update the changes on the server.
1158 manager.NotifyStateUpdatedOnServer(snapshot.update_id);
1159 ASSERT_EQ(1u, updates1.size());
1160 EXPECT_EQ(snapshot.update_id, updates1.front());
1161 ASSERT_EQ(1u, updates2.size());
1162 EXPECT_EQ(snapshot.update_id, updates2.front());
1163}
1164
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001165TEST(ComponentManager, FindComponentWithTrait) {
Alex Vakulenkoba981152015-12-05 13:58:22 -08001166 ComponentManagerImpl manager;
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001167 const char kTraits[] = R"({
1168 "trait1": {},
1169 "trait2": {},
1170 "trait3": {}
1171 })";
1172 auto traits = CreateDictionaryValue(kTraits);
1173 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1174 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1175 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
1176
1177 EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait1"));
1178 EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait2"));
1179 EXPECT_EQ("comp2", manager.FindComponentWithTrait("trait3"));
1180 EXPECT_EQ("", manager.FindComponentWithTrait("trait4"));
1181}
1182
Alex Vakulenko6b394d12015-12-05 15:52:54 -08001183TEST(ComponentManager, AddLegacyCommandAndStateDefinitions) {
1184 ComponentManagerImpl manager;
1185 const char kCommandDefs1[] = R"({
1186 "package1": {
1187 "command1": {
1188 "minimalRole": "user",
1189 "parameters": {"height": {"type": "integer"}}
1190 },
1191 "command2": {
1192 "minimalRole": "owner",
1193 "parameters": {}
1194 }
1195 },
1196 "package2": {
1197 "command1": { "minimalRole": "user" },
1198 "command2": { "minimalRole": "owner" }
1199 }
1200 })";
1201 auto json = CreateDictionaryValue(kCommandDefs1);
1202 EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1203 const char kExpected1[] = R"({
1204 "package1": {
1205 "commands": {
1206 "command1": {
1207 "minimalRole": "user",
1208 "parameters": {"height": {"type": "integer"}}
1209 },
1210 "command2": {
1211 "minimalRole": "owner",
1212 "parameters": {}
1213 }
1214 }
1215 },
1216 "package2": {
1217 "commands": {
1218 "command1": { "minimalRole": "user" },
1219 "command2": { "minimalRole": "owner" }
1220 }
1221 }
1222 })";
1223 EXPECT_JSON_EQ(kExpected1, manager.GetTraits());
1224 const char kExpectedComponents1[] = R"({
1225 "__weave__": { "traits": ["package1", "package2"] }
1226 })";
1227 EXPECT_JSON_EQ(kExpectedComponents1, manager.GetComponents());
1228
1229 const char kCommandDefs2[] = R"({
1230 "package2": {
1231 "command3": { "minimalRole": "user" }
1232 },
1233 "package3": {
1234 "command1": { "minimalRole": "user" },
1235 "command2": { "minimalRole": "owner" }
1236 }
1237 })";
1238 json = CreateDictionaryValue(kCommandDefs2);
1239 EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1240 const char kExpected2[] = R"({
1241 "package1": {
1242 "commands": {
1243 "command1": {
1244 "minimalRole": "user",
1245 "parameters": {"height": {"type": "integer"}}
1246 },
1247 "command2": {
1248 "minimalRole": "owner",
1249 "parameters": {}
1250 }
1251 }
1252 },
1253 "package2": {
1254 "commands": {
1255 "command1": { "minimalRole": "user" },
1256 "command2": { "minimalRole": "owner" },
1257 "command3": { "minimalRole": "user" }
1258 }
1259 },
1260 "package3": {
1261 "commands": {
1262 "command1": { "minimalRole": "user" },
1263 "command2": { "minimalRole": "owner" }
1264 }
1265 }
1266 })";
1267 EXPECT_JSON_EQ(kExpected2, manager.GetTraits());
1268 const char kExpectedComponents2[] = R"({
1269 "__weave__": { "traits": ["package1", "package2", "package3"] }
1270 })";
1271 EXPECT_JSON_EQ(kExpectedComponents2, manager.GetComponents());
1272
1273 // Redefining existing commands.
1274 EXPECT_FALSE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1275
1276 const char kStateDefs1[] = R"({
1277 "package1": {
1278 "prop1": { "type": "string" },
1279 "prop2": { "type": "string" }
1280 },
1281 "package4": {
1282 "prop3": { "type": "string" },
1283 "prop4": { "type": "string" }
1284 }
1285 })";
1286 json = CreateDictionaryValue(kStateDefs1);
1287 EXPECT_TRUE(manager.AddLegacyStateDefinitions(*json, nullptr));
1288 const char kExpectedComponents3[] = R"({
1289 "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1290 })";
1291 EXPECT_JSON_EQ(kExpectedComponents3, manager.GetComponents());
1292
1293 const char kExpected3[] = R"({
1294 "package1": {
1295 "commands": {
1296 "command1": {
1297 "minimalRole": "user",
1298 "parameters": {"height": {"type": "integer"}}
1299 },
1300 "command2": {
1301 "minimalRole": "owner",
1302 "parameters": {}
1303 }
1304 },
1305 "state": {
1306 "prop1": { "type": "string" },
1307 "prop2": { "type": "string" }
1308 }
1309 },
1310 "package2": {
1311 "commands": {
1312 "command1": { "minimalRole": "user" },
1313 "command2": { "minimalRole": "owner" },
1314 "command3": { "minimalRole": "user" }
1315 }
1316 },
1317 "package3": {
1318 "commands": {
1319 "command1": { "minimalRole": "user" },
1320 "command2": { "minimalRole": "owner" }
1321 }
1322 },
1323 "package4": {
1324 "state": {
1325 "prop3": { "type": "string" },
1326 "prop4": { "type": "string" }
1327 }
1328 }
1329 })";
1330 EXPECT_JSON_EQ(kExpected3, manager.GetTraits());
1331 const char kExpectedComponents4[] = R"({
1332 "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1333 })";
1334 EXPECT_JSON_EQ(kExpectedComponents4, manager.GetComponents());
1335
1336 // Redefining existing commands.
1337 EXPECT_FALSE(manager.AddLegacyStateDefinitions(*json, nullptr));
1338
1339 const char kExpected4[] = R"({
1340 "package1": {
1341 "command1": {
1342 "minimalRole": "user",
1343 "parameters": {"height": {"type": "integer"}}
1344 },
1345 "command2": {
1346 "minimalRole": "owner",
1347 "parameters": {}
1348 }
1349 },
1350 "package2": {
1351 "command1": { "minimalRole": "user" },
1352 "command2": { "minimalRole": "owner" },
1353 "command3": { "minimalRole": "user" }
1354 },
1355 "package3": {
1356 "command1": { "minimalRole": "user" },
1357 "command2": { "minimalRole": "owner" }
1358 }
1359 })";
1360 EXPECT_JSON_EQ(kExpected4, manager.GetLegacyCommandDefinitions());
1361}
1362
1363TEST(ComponentManager, GetLegacyState) {
1364 ComponentManagerImpl manager;
1365 const char kTraits[] = R"({
1366 "trait1": {
1367 "state": {
1368 "prop1": { "type": "string" },
1369 "prop2": { "type": "string" }
1370 }
1371 },
1372 "trait2": {
1373 "state": {
1374 "prop3": { "type": "string" },
1375 "prop4": { "type": "string" }
1376 }
1377 }
1378 })";
1379 auto traits = CreateDictionaryValue(kTraits);
1380 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1381 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
1382 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
1383
1384 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
1385 "comp1", R"({"trait1": {"prop1": "foo", "prop2": "bar"}})", nullptr));
1386 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
1387 "comp2", R"({"trait2": {"prop3": "baz", "prop4": "quux"}})", nullptr));
1388
1389 const char kExpected[] = R"({
1390 "trait1": {
1391 "prop1": "foo",
1392 "prop2": "bar"
1393 },
1394 "trait2": {
1395 "prop3": "baz",
1396 "prop4": "quux"
1397 }
1398 })";
1399 EXPECT_JSON_EQ(kExpected, manager.GetLegacyState());
1400}
1401
Alex Vakulenkoba981152015-12-05 13:58:22 -08001402TEST(ComponentManager, TestMockComponentManager) {
1403 // Check that all the virtual methods are mocked out.
1404 MockComponentManager mock;
1405}
1406
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08001407} // namespace weave