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