blob: 83f9933f0b95ca7525b97f27eb61be0f2d42e9c8 [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
5#include "src/component_manager.h"
6
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"
14
15namespace weave {
16
17using test::CreateDictionaryValue;
18
19namespace {
20
21bool HasTrait(const base::DictionaryValue& comp, const std::string& trait) {
22 const base::ListValue* list = nullptr;
23 if (!comp.GetList("traits", &list))
24 return false;
25 for (const base::Value* item : *list) {
26 std::string value;
27 if (item->GetAsString(&value) && value == trait)
28 return true;
29 }
30 return false;
31}
32
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080033// Creates sample trait/component trees:
34// {
35// "traits": {
36// "t1": {},
37// "t2": {},
38// "t3": {},
39// "t4": {},
40// "t5": {},
41// "t6": {},
42// },
43// "components": {
44// "comp1": {
45// "traits": [ "t1" ],
46// "components": {
47// "comp2": [
48// { "traits": [ "t2" ] },
49// {
50// "traits": [ "t3" ],
51// "components": {
52// "comp3": {
53// "traits": [ "t4" ],
54// "components": {
55// "comp4": {
56// "traits": [ "t5", "t6" ]
57// }
58// }
59// }
60// }
61// }
62// ],
63// }
64// }
65// }
66// }
67void CreateTestComponentTree(ComponentManager* manager) {
68 const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
69 auto json = CreateDictionaryValue(kTraits);
70 ASSERT_TRUE(manager->LoadTraits(*json, nullptr));
71 EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr));
72 EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t2"},
73 nullptr));
74 EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t3"},
75 nullptr));
76 EXPECT_TRUE(manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"},
77 nullptr));
78 EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4",
79 {"t5", "t6"}, nullptr));
80}
81
82// Test clock class to record predefined time intervals.
83// Implementation from base/test/simple_test_clock.{h|cc}
84class SimpleTestClock : public base::Clock {
85 public:
86 base::Time Now() override { return now_; }
87
88 // Advances the clock by |delta|.
89 void Advance(base::TimeDelta delta) { now_ += delta; }
90
91 // Sets the clock to the given time.
92 void SetNow(base::Time now) { now_ = now; }
93
94 private:
95 base::Time now_;
96};
97
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080098} // anonymous namespace
99
100TEST(ComponentManager, Empty) {
101 ComponentManager manager;
102 EXPECT_TRUE(manager.GetTraits().empty());
103 EXPECT_TRUE(manager.GetComponents().empty());
104}
105
106TEST(ComponentManager, LoadTraits) {
107 ComponentManager manager;
108 const char kTraits[] = R"({
109 "trait1": {
110 "commands": {
111 "command1": {
112 "minimalRole": "user",
113 "parameters": {"height": {"type": "integer"}}
114 }
115 },
116 "state": {
117 "property1": {"type": "boolean"}
118 }
119 },
120 "trait2": {
121 "state": {
122 "property2": {"type": "string"}
123 }
124 }
125 })";
126 auto json = CreateDictionaryValue(kTraits);
127 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
128 EXPECT_JSON_EQ(kTraits, manager.GetTraits());
129 EXPECT_TRUE(manager.GetComponents().empty());
130}
131
132TEST(ComponentManager, LoadTraitsDuplicateIdentical) {
133 ComponentManager manager;
134 const char kTraits1[] = R"({
135 "trait1": {
136 "commands": {
137 "command1": {
138 "minimalRole": "user",
139 "parameters": {"height": {"type": "integer"}}
140 }
141 },
142 "state": {
143 "property1": {"type": "boolean"}
144 }
145 },
146 "trait2": {
147 "state": {
148 "property2": {"type": "string"}
149 }
150 }
151 })";
152 auto json = CreateDictionaryValue(kTraits1);
153 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
154 const char kTraits2[] = R"({
155 "trait1": {
156 "commands": {
157 "command1": {
158 "minimalRole": "user",
159 "parameters": {"height": {"type": "integer"}}
160 }
161 },
162 "state": {
163 "property1": {"type": "boolean"}
164 }
165 },
166 "trait3": {
167 "state": {
168 "property3": {"type": "string"}
169 }
170 }
171 })";
172 json = CreateDictionaryValue(kTraits2);
173 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
174 const char kExpected[] = R"({
175 "trait1": {
176 "commands": {
177 "command1": {
178 "minimalRole": "user",
179 "parameters": {"height": {"type": "integer"}}
180 }
181 },
182 "state": {
183 "property1": {"type": "boolean"}
184 }
185 },
186 "trait2": {
187 "state": {
188 "property2": {"type": "string"}
189 }
190 },
191 "trait3": {
192 "state": {
193 "property3": {"type": "string"}
194 }
195 }
196 })";
197 EXPECT_JSON_EQ(kExpected, manager.GetTraits());
198}
199
200TEST(ComponentManager, LoadTraitsDuplicateOverride) {
201 ComponentManager manager;
202 const char kTraits1[] = R"({
203 "trait1": {
204 "commands": {
205 "command1": {
206 "minimalRole": "user",
207 "parameters": {"height": {"type": "integer"}}
208 }
209 },
210 "state": {
211 "property1": {"type": "boolean"}
212 }
213 },
214 "trait2": {
215 "state": {
216 "property2": {"type": "string"}
217 }
218 }
219 })";
220 auto json = CreateDictionaryValue(kTraits1);
221 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
222 const char kTraits2[] = R"({
223 "trait1": {
224 "commands": {
225 "command1": {
226 "minimalRole": "user",
227 "parameters": {"height": {"type": "string"}}
228 }
229 },
230 "state": {
231 "property1": {"type": "boolean"}
232 }
233 },
234 "trait3": {
235 "state": {
236 "property3": {"type": "string"}
237 }
238 }
239 })";
240 json = CreateDictionaryValue(kTraits2);
241 EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
242}
243
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800244TEST(ComponentManager, AddTraitDefChangedCallback) {
245 ComponentManager manager;
246 int count = 0;
247 int count2 = 0;
248 manager.AddTraitDefChangedCallback(base::Bind([&count]() { count++; }));
249 manager.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; }));
250 EXPECT_EQ(1, count);
251 EXPECT_EQ(1, count2);
252 // New definitions.
253 const char kTraits1[] = R"({
254 "trait1": {
255 "state": {
256 "property1": {"type": "boolean"}
257 }
258 },
259 "trait2": {
260 "state": {
261 "property2": {"type": "string"}
262 }
263 }
264 })";
265 auto json = CreateDictionaryValue(kTraits1);
266 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
267 EXPECT_EQ(2, count);
268 // Duplicate definition, shouldn't call the callback.
269 const char kTraits2[] = R"({
270 "trait1": {
271 "state": {
272 "property1": {"type": "boolean"}
273 }
274 }
275 })";
276 json = CreateDictionaryValue(kTraits2);
277 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
278 EXPECT_EQ(2, count);
279 // New definition, should call the callback now.
280 const char kTraits3[] = R"({
281 "trait3": {
282 "state": {
283 "property3": {"type": "string"}
284 }
285 }
286 })";
287 json = CreateDictionaryValue(kTraits3);
288 EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
289 EXPECT_EQ(3, count);
290 // Wrong definition, shouldn't call the callback.
291 const char kTraits4[] = R"({
292 "trait4": "foo"
293 })";
294 json = CreateDictionaryValue(kTraits4);
295 EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
296 EXPECT_EQ(3, count);
297 // Make sure both callbacks were called the same number of times.
298 EXPECT_EQ(count2, count);
299}
300
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800301TEST(ComponentManager, LoadTraitsNotAnObject) {
302 ComponentManager manager;
303 const char kTraits1[] = R"({"trait1": 0})";
304 auto json = CreateDictionaryValue(kTraits1);
305 ErrorPtr error;
306 EXPECT_FALSE(manager.LoadTraits(*json, &error));
307 EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
308}
309
310TEST(ComponentManager, FindTraitDefinition) {
311 ComponentManager manager;
312 const char kTraits[] = R"({
313 "trait1": {
314 "commands": {
315 "command1": {
316 "minimalRole": "user",
317 "parameters": {"height": {"type": "integer"}}
318 }
319 },
320 "state": {
321 "property1": {"type": "boolean"}
322 }
323 },
324 "trait2": {
325 "state": {
326 "property2": {"type": "string"}
327 }
328 }
329 })";
330 auto json = CreateDictionaryValue(kTraits);
331 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
332
333 const base::DictionaryValue* trait = manager.FindTraitDefinition("trait1");
334 ASSERT_NE(nullptr, trait);
335 const char kExpected1[] = R"({
336 "commands": {
337 "command1": {
338 "minimalRole": "user",
339 "parameters": {"height": {"type": "integer"}}
340 }
341 },
342 "state": {
343 "property1": {"type": "boolean"}
344 }
345 })";
346 EXPECT_JSON_EQ(kExpected1, *trait);
347
348 trait = manager.FindTraitDefinition("trait2");
349 ASSERT_NE(nullptr, trait);
350 const char kExpected2[] = R"({
351 "state": {
352 "property2": {"type": "string"}
353 }
354 })";
355 EXPECT_JSON_EQ(kExpected2, *trait);
356
357 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3"));
358}
359
360TEST(ComponentManager, FindCommandDefinition) {
361 ComponentManager manager;
362 const char kTraits[] = R"({
363 "trait1": {
364 "commands": {
365 "command1": {
366 "minimalRole": "user",
367 "parameters": {"height": {"type": "integer"}}
368 }
369 }
370 },
371 "trait2": {
372 "commands": {
373 "command1": {
374 "minimalRole": "manager"
375 },
376 "command2": {
377 "minimalRole": "owner"
378 }
379 }
380 }
381 })";
382 auto json = CreateDictionaryValue(kTraits);
383 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
384
385 const auto* cmd_def = manager.FindCommandDefinition("trait1.command1");
386 ASSERT_NE(nullptr, cmd_def);
387 const char kExpected1[] = R"({
388 "minimalRole": "user",
389 "parameters": {"height": {"type": "integer"}}
390 })";
391 EXPECT_JSON_EQ(kExpected1, *cmd_def);
392
393 cmd_def = manager.FindCommandDefinition("trait2.command1");
394 ASSERT_NE(nullptr, cmd_def);
395 const char kExpected2[] = R"({
396 "minimalRole": "manager"
397 })";
398 EXPECT_JSON_EQ(kExpected2, *cmd_def);
399
400 cmd_def = manager.FindCommandDefinition("trait2.command2");
401 ASSERT_NE(nullptr, cmd_def);
402 const char kExpected3[] = R"({
403 "minimalRole": "owner"
404 })";
405 EXPECT_JSON_EQ(kExpected3, *cmd_def);
406
407 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command2"));
408 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3.command1"));
409 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait"));
410 EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command1.parameters"));
411}
412
413TEST(ComponentManager, GetMinimalRole) {
414 ComponentManager manager;
415 const char kTraits[] = R"({
416 "trait1": {
417 "commands": {
418 "command1": { "minimalRole": "user" },
419 "command2": { "minimalRole": "viewer" }
420 }
421 },
422 "trait2": {
423 "commands": {
424 "command1": { "minimalRole": "manager" },
425 "command2": { "minimalRole": "owner" }
426 }
427 }
428 })";
429 auto json = CreateDictionaryValue(kTraits);
430 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
431
432 UserRole role;
433 ASSERT_TRUE(manager.GetMinimalRole("trait1.command1", &role, nullptr));
434 EXPECT_EQ(UserRole::kUser, role);
435
436 ASSERT_TRUE(manager.GetMinimalRole("trait1.command2", &role, nullptr));
437 EXPECT_EQ(UserRole::kViewer, role);
438
439 ASSERT_TRUE(manager.GetMinimalRole("trait2.command1", &role, nullptr));
440 EXPECT_EQ(UserRole::kManager, role);
441
442 ASSERT_TRUE(manager.GetMinimalRole("trait2.command2", &role, nullptr));
443 EXPECT_EQ(UserRole::kOwner, role);
444
445 EXPECT_FALSE(manager.GetMinimalRole("trait1.command3", &role, nullptr));
446}
447
448TEST(ComponentManager, AddComponent) {
449 ComponentManager manager;
450 const char kTraits[] = R"({"trait1": {}, "trait2": {}, "trait3": {}})";
451 auto json = CreateDictionaryValue(kTraits);
452 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
453 EXPECT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
454 EXPECT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
455 const char kExpected[] = R"({
456 "comp1": {
457 "traits": ["trait1", "trait2"]
458 },
459 "comp2": {
460 "traits": ["trait3"]
461 }
462 })";
463 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
464
465 // 'trait4' is undefined, so can't add a component referring to it.
466 EXPECT_FALSE(manager.AddComponent("", "comp3", {"trait4"}, nullptr));
467}
468
469TEST(ComponentManager, AddSubComponent) {
470 ComponentManager manager;
471 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
472 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
473 EXPECT_TRUE(manager.AddComponent("comp1", "comp3", {}, nullptr));
474 EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
475 const char kExpected[] = R"({
476 "comp1": {
477 "traits": [],
478 "components": {
479 "comp2": {
480 "traits": [],
481 "components": {
482 "comp4": {
483 "traits": []
484 }
485 }
486 },
487 "comp3": {
488 "traits": []
489 }
490 }
491 }
492 })";
493 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
494}
495
496TEST(ComponentManager, AddComponentArrayItem) {
497 ComponentManager manager;
498 const char kTraits[] = R"({"foo": {}, "bar": {}})";
499 auto json = CreateDictionaryValue(kTraits);
500 ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
501
502 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
503 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"foo"},
504 nullptr));
505 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"bar"},
506 nullptr));
507 EXPECT_TRUE(manager.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr));
508 EXPECT_TRUE(manager.AddComponent("comp1.comp2[1].comp3", "comp4", {},
509 nullptr));
510 const char kExpected[] = R"({
511 "comp1": {
512 "traits": [],
513 "components": {
514 "comp2": [
515 {
516 "traits": ["foo"]
517 },
518 {
519 "traits": ["bar"],
520 "components": {
521 "comp3": {
522 "traits": [],
523 "components": {
524 "comp4": {
525 "traits": []
526 }
527 }
528 }
529 }
530 }
531 ]
532 }
533 }
534 })";
535 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
536}
537
538TEST(ComponentManager, AddComponentExist) {
539 ComponentManager manager;
540 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
541 EXPECT_FALSE(manager.AddComponent("", "comp1", {}, nullptr));
542 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
543 EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
544}
545
546TEST(ComponentManager, AddComponentDoesNotExist) {
547 ComponentManager manager;
548 EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
549}
550
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800551TEST(ComponentManager, AddComponentTreeChangedCallback) {
552 ComponentManager manager;
553 int count = 0;
554 int count2 = 0;
555 manager.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; }));
556 manager.AddComponentTreeChangedCallback(
557 base::Bind([&count2]() { count2++; }));
558 EXPECT_EQ(1, count);
559 EXPECT_EQ(1, count2);
560 EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
561 EXPECT_EQ(2, count);
562 EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
563 EXPECT_EQ(3, count);
564 EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
565 EXPECT_EQ(4, count);
566 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
567 EXPECT_EQ(5, count);
568 EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
569 EXPECT_EQ(6, count);
570 // Make sure both callbacks were called the same number of times.
571 EXPECT_EQ(count2, count);
572}
573
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800574TEST(ComponentManager, FindComponent) {
575 ComponentManager manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800576 CreateTestComponentTree(&manager);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800577
578 const base::DictionaryValue* comp = manager.FindComponent("comp1", nullptr);
579 ASSERT_NE(nullptr, comp);
580 EXPECT_TRUE(HasTrait(*comp, "t1"));
581
582 comp = manager.FindComponent("comp1.comp2[0]", nullptr);
583 ASSERT_NE(nullptr, comp);
584 EXPECT_TRUE(HasTrait(*comp, "t2"));
585
586 comp = manager.FindComponent("comp1.comp2[1]", nullptr);
587 ASSERT_NE(nullptr, comp);
588 EXPECT_TRUE(HasTrait(*comp, "t3"));
589
590 comp = manager.FindComponent("comp1.comp2[1].comp3", nullptr);
591 ASSERT_NE(nullptr, comp);
592 EXPECT_TRUE(HasTrait(*comp, "t4"));
593
594 comp = manager.FindComponent("comp1.comp2[1].comp3.comp4", nullptr);
595 ASSERT_NE(nullptr, comp);
596 EXPECT_TRUE(HasTrait(*comp, "t5"));
597
598 // Some whitespaces don't hurt.
599 comp = manager.FindComponent(" comp1 . comp2 [ \t 1 ] . comp3.comp4 ",
600 nullptr);
601 EXPECT_NE(nullptr, comp);
602
603 // Now check some failure cases.
604 ErrorPtr error;
605 EXPECT_EQ(nullptr, manager.FindComponent("", &error));
606 EXPECT_NE(nullptr, error.get());
607 // 'comp2' doesn't exist:
608 EXPECT_EQ(nullptr, manager.FindComponent("comp2", nullptr));
609 // 'comp1.comp2' is an array, not a component:
610 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2", nullptr));
611 // 'comp1.comp2[3]' doesn't exist:
612 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[3]", nullptr));
613 // Empty component names:
614 EXPECT_EQ(nullptr, manager.FindComponent(".comp2[1]", nullptr));
615 EXPECT_EQ(nullptr, manager.FindComponent("comp1.[1]", nullptr));
616 // Invalid array indices:
617 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[s]", nullptr));
618 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[-2]", nullptr));
619 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1e1]", nullptr));
620 EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1", nullptr));
621}
622
623TEST(ComponentManager, AddCommand) {
624 ComponentManager manager;
625 const char kTraits[] = R"({
626 "trait1": {
627 "commands": {
628 "command1": { "minimalRole": "user" },
629 "command2": { "minimalRole": "viewer" }
630 }
631 },
632 "trait2": {
633 "commands": {
634 "command1": { "minimalRole": "manager" },
635 "command2": { "minimalRole": "owner" }
636 }
637 }
638 })";
639 auto traits = CreateDictionaryValue(kTraits);
640 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
641 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
642 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
643
644 std::string id;
645 const char kCommand1[] = R"({
646 "name": "trait1.command1",
647 "component": "comp1",
648 "parameters": {}
649 })";
650 auto command1 = CreateDictionaryValue(kCommand1);
651 EXPECT_TRUE(manager.AddCommand(*command1, UserRole::kUser, &id, nullptr));
652 // Not enough access rights
653 EXPECT_FALSE(manager.AddCommand(*command1, UserRole::kViewer, &id, nullptr));
654
655 const char kCommand2[] = R"({
656 "name": "trait1.command3",
657 "component": "comp1",
658 "parameters": {}
659 })";
660 auto command2 = CreateDictionaryValue(kCommand2);
661 // trait1.command3 doesn't exist
662 EXPECT_FALSE(manager.AddCommand(*command2, UserRole::kOwner, &id, nullptr));
663
664 const char kCommand3[] = R"({
665 "name": "trait2.command1",
666 "component": "comp1",
667 "parameters": {}
668 })";
669 auto command3 = CreateDictionaryValue(kCommand3);
670 // Component comp1 doesn't have trait2.
671 EXPECT_FALSE(manager.AddCommand(*command3, UserRole::kOwner, &id, nullptr));
672
673 // No component specified, use the first top-level component (comp1)
674 const char kCommand4[] = R"({
675 "name": "trait1.command1",
676 "parameters": {}
677 })";
678 auto command4 = CreateDictionaryValue(kCommand4);
679 EXPECT_TRUE(manager.AddCommand(*command4, UserRole::kOwner, &id, nullptr));
680 auto cmd = manager.FindCommand(id);
681 ASSERT_NE(nullptr, cmd);
682 EXPECT_EQ("comp1", cmd->GetComponent());
683}
684
685TEST(ComponentManager, AddCommandHandler) {
686 ComponentManager manager;
687 const char kTraits[] = R"({
688 "trait1": {
689 "commands": {
690 "command1": { "minimalRole": "user" }
691 }
692 },
693 "trait2": {
694 "commands": {
695 "command2": { "minimalRole": "user" }
696 }
697 }
698 })";
699 auto traits = CreateDictionaryValue(kTraits);
700 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
701 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
702 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
703
704 std::string last_tags;
705 auto handler = [&last_tags](int tag, const std::weak_ptr<Command>& command) {
706 if (!last_tags.empty())
707 last_tags += ',';
708 last_tags += std::to_string(tag);
709 };
710
711 manager.AddCommandHandler("comp1", "trait1.command1", base::Bind(handler, 1));
712 manager.AddCommandHandler("comp2", "trait1.command1", base::Bind(handler, 2));
713 manager.AddCommandHandler("comp2", "trait2.command2", base::Bind(handler, 3));
714 EXPECT_TRUE(last_tags.empty());
715
716 const char kCommand1[] = R"({
717 "name": "trait1.command1",
718 "component": "comp1"
719 })";
720 auto command1 = CreateDictionaryValue(kCommand1);
721 EXPECT_TRUE(manager.AddCommand(*command1, UserRole::kUser, nullptr, nullptr));
722 EXPECT_EQ("1", last_tags);
723 last_tags.clear();
724
725 const char kCommand2[] = R"({
726 "name": "trait1.command1",
727 "component": "comp2"
728 })";
729 auto command2 = CreateDictionaryValue(kCommand2);
730 EXPECT_TRUE(manager.AddCommand(*command2, UserRole::kUser, nullptr, nullptr));
731 EXPECT_EQ("2", last_tags);
732 last_tags.clear();
733
734 const char kCommand3[] = R"({
735 "name": "trait2.command2",
736 "component": "comp2",
737 "parameters": {}
738 })";
739 auto command3 = CreateDictionaryValue(kCommand3);
740 EXPECT_TRUE(manager.AddCommand(*command3, UserRole::kUser, nullptr, nullptr));
741 EXPECT_EQ("3", last_tags);
742 last_tags.clear();
743}
744
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800745TEST(ComponentManager, SetStateProperties) {
746 ComponentManager manager;
747 CreateTestComponentTree(&manager);
748
749 const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
750 auto state1 = CreateDictionaryValue(kState1);
751 ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr));
752 const char kExpected1[] = R"({
753 "comp1": {
754 "traits": [ "t1" ],
755 "state": {"t1": {"p1": 0, "p2": "foo"}},
756 "components": {
757 "comp2": [
758 {
759 "traits": [ "t2" ]
760 },
761 {
762 "traits": [ "t3" ],
763 "components": {
764 "comp3": {
765 "traits": [ "t4" ],
766 "components": {
767 "comp4": {
768 "traits": [ "t5", "t6" ]
769 }
770 }
771 }
772 }
773 }
774 ]
775 }
776 }
777 })";
778 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
779
780 const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
781 auto state2 = CreateDictionaryValue(kState2);
782 ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr));
783
784 const char kExpected2[] = R"({
785 "comp1": {
786 "traits": [ "t1" ],
787 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
788 "components": {
789 "comp2": [
790 {
791 "traits": [ "t2" ]
792 },
793 {
794 "traits": [ "t3" ],
795 "components": {
796 "comp3": {
797 "traits": [ "t4" ],
798 "components": {
799 "comp4": {
800 "traits": [ "t5", "t6" ]
801 }
802 }
803 }
804 }
805 }
806 ]
807 }
808 }
809 })";
810 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
811
812 const char kState3[] = R"({"t5": {"p1": 1}})";
813 auto state3 = CreateDictionaryValue(kState3);
814 ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
815 nullptr));
816
817 const char kExpected3[] = R"({
818 "comp1": {
819 "traits": [ "t1" ],
820 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
821 "components": {
822 "comp2": [
823 {
824 "traits": [ "t2" ]
825 },
826 {
827 "traits": [ "t3" ],
828 "components": {
829 "comp3": {
830 "traits": [ "t4" ],
831 "components": {
832 "comp4": {
833 "traits": [ "t5", "t6" ],
834 "state": { "t5": { "p1": 1 } }
835 }
836 }
837 }
838 }
839 }
840 ]
841 }
842 }
843 })";
844 EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
845}
846
847TEST(ComponentManager, SetStatePropertiesFromJson) {
848 ComponentManager manager;
849 CreateTestComponentTree(&manager);
850
851 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
852 "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})",
853 nullptr));
854
855 const char kExpected[] = R"({
856 "comp1": {
857 "traits": [ "t1" ],
858 "components": {
859 "comp2": [
860 {
861 "traits": [ "t2" ]
862 },
863 {
864 "traits": [ "t3" ],
865 "components": {
866 "comp3": {
867 "traits": [ "t4" ],
868 "components": {
869 "comp4": {
870 "traits": [ "t5", "t6" ],
871 "state": {
872 "t5": { "p1": 3 },
873 "t6": { "p2": 5 }
874 }
875 }
876 }
877 }
878 }
879 }
880 ]
881 }
882 }
883 })";
884 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
885}
886
887TEST(ComponentManager, SetGetStateProperty) {
888 ComponentManager manager;
889 const char kTraits[] = R"({
890 "trait1": {
891 "state": {
892 "prop1": { "type": "string" },
893 "prop2": { "type": "integer" }
894 }
895 },
896 "trait2": {
897 "state": {
898 "prop3": { "type": "string" },
899 "prop4": { "type": "string" }
900 }
901 }
902 })";
903 auto traits = CreateDictionaryValue(kTraits);
904 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
905 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
906
907 base::StringValue p1("foo");
908 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
909
910 const char kExpected1[] = R"({
911 "comp1": {
912 "traits": [ "trait1", "trait2" ],
913 "state": {
914 "trait1": { "prop1": "foo" }
915 }
916 }
917 })";
918 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
919
920 base::FundamentalValue p2(2);
921 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
922
923 const char kExpected2[] = R"({
924 "comp1": {
925 "traits": [ "trait1", "trait2" ],
926 "state": {
927 "trait1": { "prop1": "foo" },
928 "trait2": { "prop3": 2 }
929 }
930 }
931 })";
932 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
933 // Just the package name without property:
934 EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr));
935
936 const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1",
937 nullptr);
938 ASSERT_NE(nullptr, value);
939 EXPECT_TRUE(p1.Equals(value));
940 value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr);
941 ASSERT_NE(nullptr, value);
942 EXPECT_TRUE(p2.Equals(value));
943
944 // Non-existing property:
945 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr));
946 // Non-existing component
947 EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr));
948 // Just the package name without property:
949 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr));
950}
951
952TEST(ComponentManager, AddStateChangedCallback) {
953 SimpleTestClock clock;
954 ComponentManager manager{&clock};
955 const char kTraits[] = R"({
956 "trait1": {
957 "state": {
958 "prop1": { "type": "string" },
959 "prop2": { "type": "string" }
960 }
961 }
962 })";
963 auto traits = CreateDictionaryValue(kTraits);
964 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
965 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
966
967 int count = 0;
968 int count2 = 0;
969 manager.AddStateChangedCallback(base::Bind([&count]() { count++; }));
970 manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
971 EXPECT_EQ(1, count);
972 EXPECT_EQ(1, count2);
973 EXPECT_EQ(0, manager.GetLastStateChangeId());
974
975 base::StringValue p1("foo");
976 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
977 EXPECT_EQ(2, count);
978 EXPECT_EQ(2, count2);
979 EXPECT_EQ(1, manager.GetLastStateChangeId());
980
981 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
982 EXPECT_EQ(3, count);
983 EXPECT_EQ(3, count2);
984 EXPECT_EQ(2, manager.GetLastStateChangeId());
985
986 // Fail - no component.
987 ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
988 EXPECT_EQ(3, count);
989 EXPECT_EQ(3, count2);
990 EXPECT_EQ(2, manager.GetLastStateChangeId());
991}
992
993TEST(ComponentManager, ComponentStateUpdates) {
994 SimpleTestClock clock;
995 ComponentManager manager{&clock};
996 const char kTraits[] = R"({
997 "trait1": {
998 "state": {
999 "prop1": { "type": "string" },
1000 "prop2": { "type": "string" }
1001 }
1002 },
1003 "trait2": {
1004 "state": {
1005 "prop3": { "type": "string" },
1006 "prop4": { "type": "string" }
1007 }
1008 }
1009 })";
1010 auto traits = CreateDictionaryValue(kTraits);
1011 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1012 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1013 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
1014
1015 std::vector<ComponentManager::UpdateID> updates1;
1016 auto callback1 = [&updates1](ComponentManager::UpdateID id) {
1017 updates1.push_back(id);
1018 };
1019 // State change queue is empty, callback should be called immediately.
1020 auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1));
1021 ASSERT_EQ(1u, updates1.size());
1022 EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front());
1023 updates1.clear();
1024
1025 base::StringValue foo("foo");
1026 base::Time time1 = base::Time::Now();
1027 clock.SetNow(time1);
1028 // These three updates should be grouped into two separate state change queue
1029 // items, since they all happen at the same time, but for two different
1030 // components.
1031 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
1032 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
1033 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
1034
1035 std::vector<ComponentManager::UpdateID> updates2;
1036 auto callback2 = [&updates2](ComponentManager::UpdateID id) {
1037 updates2.push_back(id);
1038 };
1039 // State change queue is not empty, so callback will be called later.
1040 auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2));
1041 EXPECT_TRUE(updates2.empty());
1042
1043 base::StringValue bar("bar");
1044 base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
1045 clock.SetNow(time2);
1046 // Two more update events (as above) but at |time2|.
1047 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
1048 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
1049 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
1050
1051 auto snapshot = manager.GetAndClearRecordedStateChanges();
1052 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id);
1053 ASSERT_EQ(4u, snapshot.state_changes.size());
1054
1055 EXPECT_EQ("comp1", snapshot.state_changes[0].component);
1056 EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
1057 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
1058 *snapshot.state_changes[0].changed_properties);
1059
1060 EXPECT_EQ("comp2", snapshot.state_changes[1].component);
1061 EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
1062 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
1063 *snapshot.state_changes[1].changed_properties);
1064
1065 EXPECT_EQ("comp1", snapshot.state_changes[2].component);
1066 EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
1067 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
1068 *snapshot.state_changes[2].changed_properties);
1069
1070 EXPECT_EQ("comp2", snapshot.state_changes[3].component);
1071 EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
1072 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
1073 *snapshot.state_changes[3].changed_properties);
1074
1075 // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
1076 auto snapshot2 = manager.GetAndClearRecordedStateChanges();
1077 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id);
1078 EXPECT_TRUE(snapshot2.state_changes.empty());
1079
1080 // Now indicate that we have update the changes on the server.
1081 manager.NotifyStateUpdatedOnServer(snapshot.update_id);
1082 ASSERT_EQ(1u, updates1.size());
1083 EXPECT_EQ(snapshot.update_id, updates1.front());
1084 ASSERT_EQ(1u, updates2.size());
1085 EXPECT_EQ(snapshot.update_id, updates2.front());
1086}
1087
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08001088} // namespace weave