blob: 902ee15de40356a4d567ab8c4e91e17b9651bc6c [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 Vakulenko20334332015-12-08 13:19:49 -0800822TEST(ComponentManager, AddDefaultCommandHandler) {
823 ComponentManagerImpl manager;
824 const char kTraits[] = R"({
825 "trait1": {
826 "commands": {
827 "command1": { "minimalRole": "user" }
828 }
829 },
830 "trait2": {
831 "commands": {
832 "command2": { "minimalRole": "user" }
833 }
834 }
835 })";
836 auto traits = CreateDictionaryValue(kTraits);
837 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
838 ASSERT_TRUE(manager.AddComponent("", "comp", {"trait1", "trait2"}, nullptr));
839
840 int count = 0;
841 auto handler = [&count](int tag, const std::weak_ptr<Command>& command) {
842 count++;
843 };
844
845 manager.AddCommandHandler("", "", base::Bind(handler, 1));
846 EXPECT_EQ(0, count);
847
848 const char kCommand1[] = R"({
849 "name": "trait1.command1",
850 "component": "comp"
851 })";
852 auto command1 = CreateDictionaryValue(kCommand1);
853 auto command_instance = manager.ParseCommandInstance(
854 *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
855 ASSERT_NE(nullptr, command_instance.get());
856 manager.AddCommand(std::move(command_instance));
857 EXPECT_EQ(1, count);
858
859 const char kCommand2[] = R"({
860 "name": "trait2.command2",
861 "component": "comp"
862 })";
863 auto command2 = CreateDictionaryValue(kCommand2);
864 command_instance = manager.ParseCommandInstance(
865 *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
866 ASSERT_NE(nullptr, command_instance.get());
867 manager.AddCommand(std::move(command_instance));
868 EXPECT_EQ(2, count);
869}
870
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800871TEST(ComponentManager, SetStateProperties) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800872 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800873 CreateTestComponentTree(&manager);
874
875 const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
876 auto state1 = CreateDictionaryValue(kState1);
877 ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr));
878 const char kExpected1[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800879 "comp1": {
880 "traits": [ "t1" ],
881 "state": {"t1": {"p1": 0, "p2": "foo"}},
882 "components": {
883 "comp2": [
884 {
885 "traits": [ "t2" ]
886 },
887 {
888 "traits": [ "t3" ],
889 "components": {
890 "comp3": {
891 "traits": [ "t4" ],
892 "components": {
893 "comp4": {
894 "traits": [ "t5", "t6" ]
895 }
896 }
897 }
898 }
899 }
900 ]
901 }
902 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800903 })";
904 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
905
906 const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
907 auto state2 = CreateDictionaryValue(kState2);
908 ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr));
909
910 const char kExpected2[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800911 "comp1": {
912 "traits": [ "t1" ],
913 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
914 "components": {
915 "comp2": [
916 {
917 "traits": [ "t2" ]
918 },
919 {
920 "traits": [ "t3" ],
921 "components": {
922 "comp3": {
923 "traits": [ "t4" ],
924 "components": {
925 "comp4": {
926 "traits": [ "t5", "t6" ]
927 }
928 }
929 }
930 }
931 }
932 ]
933 }
934 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800935 })";
936 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
937
938 const char kState3[] = R"({"t5": {"p1": 1}})";
939 auto state3 = CreateDictionaryValue(kState3);
940 ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
941 nullptr));
942
943 const char kExpected3[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800944 "comp1": {
945 "traits": [ "t1" ],
946 "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
947 "components": {
948 "comp2": [
949 {
950 "traits": [ "t2" ]
951 },
952 {
953 "traits": [ "t3" ],
954 "components": {
955 "comp3": {
956 "traits": [ "t4" ],
957 "components": {
958 "comp4": {
959 "traits": [ "t5", "t6" ],
960 "state": { "t5": { "p1": 1 } }
961 }
962 }
963 }
964 }
965 }
966 ]
967 }
968 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800969 })";
970 EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
971}
972
973TEST(ComponentManager, SetStatePropertiesFromJson) {
Alex Vakulenkoba981152015-12-05 13:58:22 -0800974 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800975 CreateTestComponentTree(&manager);
976
977 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
978 "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})",
979 nullptr));
980
981 const char kExpected[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800982 "comp1": {
983 "traits": [ "t1" ],
984 "components": {
985 "comp2": [
986 {
987 "traits": [ "t2" ]
988 },
989 {
990 "traits": [ "t3" ],
991 "components": {
992 "comp3": {
993 "traits": [ "t4" ],
994 "components": {
995 "comp4": {
996 "traits": [ "t5", "t6" ],
997 "state": {
998 "t5": { "p1": 3 },
999 "t6": { "p2": 5 }
1000 }
1001 }
1002 }
1003 }
1004 }
1005 }
1006 ]
1007 }
1008 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001009 })";
1010 EXPECT_JSON_EQ(kExpected, manager.GetComponents());
1011}
1012
1013TEST(ComponentManager, SetGetStateProperty) {
Alex Vakulenkoba981152015-12-05 13:58:22 -08001014 ComponentManagerImpl manager;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001015 const char kTraits[] = R"({
1016 "trait1": {
1017 "state": {
1018 "prop1": { "type": "string" },
1019 "prop2": { "type": "integer" }
1020 }
1021 },
1022 "trait2": {
1023 "state": {
1024 "prop3": { "type": "string" },
1025 "prop4": { "type": "string" }
1026 }
1027 }
1028 })";
1029 auto traits = CreateDictionaryValue(kTraits);
1030 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1031 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1032
1033 base::StringValue p1("foo");
1034 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
1035
1036 const char kExpected1[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001037 "comp1": {
1038 "traits": [ "trait1", "trait2" ],
1039 "state": {
1040 "trait1": { "prop1": "foo" }
1041 }
1042 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001043 })";
1044 EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
1045
1046 base::FundamentalValue p2(2);
1047 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
1048
1049 const char kExpected2[] = R"({
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001050 "comp1": {
1051 "traits": [ "trait1", "trait2" ],
1052 "state": {
1053 "trait1": { "prop1": "foo" },
1054 "trait2": { "prop3": 2 }
1055 }
1056 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001057 })";
1058 EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
1059 // Just the package name without property:
1060 EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr));
1061
1062 const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1",
1063 nullptr);
1064 ASSERT_NE(nullptr, value);
1065 EXPECT_TRUE(p1.Equals(value));
1066 value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr);
1067 ASSERT_NE(nullptr, value);
1068 EXPECT_TRUE(p2.Equals(value));
1069
1070 // Non-existing property:
1071 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr));
1072 // Non-existing component
1073 EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr));
1074 // Just the package name without property:
1075 EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr));
1076}
1077
1078TEST(ComponentManager, AddStateChangedCallback) {
1079 SimpleTestClock clock;
Alex Vakulenkoba981152015-12-05 13:58:22 -08001080 ComponentManagerImpl manager{&clock};
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001081 const char kTraits[] = R"({
1082 "trait1": {
1083 "state": {
1084 "prop1": { "type": "string" },
1085 "prop2": { "type": "string" }
1086 }
1087 }
1088 })";
1089 auto traits = CreateDictionaryValue(kTraits);
1090 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1091 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
1092
1093 int count = 0;
1094 int count2 = 0;
1095 manager.AddStateChangedCallback(base::Bind([&count]() { count++; }));
1096 manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
1097 EXPECT_EQ(1, count);
1098 EXPECT_EQ(1, count2);
1099 EXPECT_EQ(0, manager.GetLastStateChangeId());
1100
1101 base::StringValue p1("foo");
1102 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
1103 EXPECT_EQ(2, count);
1104 EXPECT_EQ(2, count2);
1105 EXPECT_EQ(1, manager.GetLastStateChangeId());
1106
1107 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
1108 EXPECT_EQ(3, count);
1109 EXPECT_EQ(3, count2);
1110 EXPECT_EQ(2, manager.GetLastStateChangeId());
1111
1112 // Fail - no component.
1113 ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
1114 EXPECT_EQ(3, count);
1115 EXPECT_EQ(3, count2);
1116 EXPECT_EQ(2, manager.GetLastStateChangeId());
1117}
1118
1119TEST(ComponentManager, ComponentStateUpdates) {
1120 SimpleTestClock clock;
Alex Vakulenkoba981152015-12-05 13:58:22 -08001121 ComponentManagerImpl manager{&clock};
Alex Vakulenko7b588fc2015-12-04 16:03:59 -08001122 const char kTraits[] = R"({
1123 "trait1": {
1124 "state": {
1125 "prop1": { "type": "string" },
1126 "prop2": { "type": "string" }
1127 }
1128 },
1129 "trait2": {
1130 "state": {
1131 "prop3": { "type": "string" },
1132 "prop4": { "type": "string" }
1133 }
1134 }
1135 })";
1136 auto traits = CreateDictionaryValue(kTraits);
1137 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1138 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1139 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
1140
1141 std::vector<ComponentManager::UpdateID> updates1;
1142 auto callback1 = [&updates1](ComponentManager::UpdateID id) {
1143 updates1.push_back(id);
1144 };
1145 // State change queue is empty, callback should be called immediately.
1146 auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1));
1147 ASSERT_EQ(1u, updates1.size());
1148 EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front());
1149 updates1.clear();
1150
1151 base::StringValue foo("foo");
1152 base::Time time1 = base::Time::Now();
1153 clock.SetNow(time1);
1154 // These three updates should be grouped into two separate state change queue
1155 // items, since they all happen at the same time, but for two different
1156 // components.
1157 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
1158 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
1159 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
1160
1161 std::vector<ComponentManager::UpdateID> updates2;
1162 auto callback2 = [&updates2](ComponentManager::UpdateID id) {
1163 updates2.push_back(id);
1164 };
1165 // State change queue is not empty, so callback will be called later.
1166 auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2));
1167 EXPECT_TRUE(updates2.empty());
1168
1169 base::StringValue bar("bar");
1170 base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
1171 clock.SetNow(time2);
1172 // Two more update events (as above) but at |time2|.
1173 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
1174 ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
1175 ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
1176
1177 auto snapshot = manager.GetAndClearRecordedStateChanges();
1178 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id);
1179 ASSERT_EQ(4u, snapshot.state_changes.size());
1180
1181 EXPECT_EQ("comp1", snapshot.state_changes[0].component);
1182 EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
1183 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
1184 *snapshot.state_changes[0].changed_properties);
1185
1186 EXPECT_EQ("comp2", snapshot.state_changes[1].component);
1187 EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
1188 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
1189 *snapshot.state_changes[1].changed_properties);
1190
1191 EXPECT_EQ("comp1", snapshot.state_changes[2].component);
1192 EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
1193 EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
1194 *snapshot.state_changes[2].changed_properties);
1195
1196 EXPECT_EQ("comp2", snapshot.state_changes[3].component);
1197 EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
1198 EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
1199 *snapshot.state_changes[3].changed_properties);
1200
1201 // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
1202 auto snapshot2 = manager.GetAndClearRecordedStateChanges();
1203 EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id);
1204 EXPECT_TRUE(snapshot2.state_changes.empty());
1205
1206 // Now indicate that we have update the changes on the server.
1207 manager.NotifyStateUpdatedOnServer(snapshot.update_id);
1208 ASSERT_EQ(1u, updates1.size());
1209 EXPECT_EQ(snapshot.update_id, updates1.front());
1210 ASSERT_EQ(1u, updates2.size());
1211 EXPECT_EQ(snapshot.update_id, updates2.front());
1212}
1213
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001214TEST(ComponentManager, FindComponentWithTrait) {
Alex Vakulenkoba981152015-12-05 13:58:22 -08001215 ComponentManagerImpl manager;
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -08001216 const char kTraits[] = R"({
1217 "trait1": {},
1218 "trait2": {},
1219 "trait3": {}
1220 })";
1221 auto traits = CreateDictionaryValue(kTraits);
1222 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1223 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
1224 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
1225
1226 EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait1"));
1227 EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait2"));
1228 EXPECT_EQ("comp2", manager.FindComponentWithTrait("trait3"));
1229 EXPECT_EQ("", manager.FindComponentWithTrait("trait4"));
1230}
1231
Alex Vakulenko6b394d12015-12-05 15:52:54 -08001232TEST(ComponentManager, AddLegacyCommandAndStateDefinitions) {
1233 ComponentManagerImpl manager;
1234 const char kCommandDefs1[] = R"({
1235 "package1": {
1236 "command1": {
1237 "minimalRole": "user",
1238 "parameters": {"height": {"type": "integer"}}
1239 },
1240 "command2": {
1241 "minimalRole": "owner",
1242 "parameters": {}
1243 }
1244 },
1245 "package2": {
1246 "command1": { "minimalRole": "user" },
1247 "command2": { "minimalRole": "owner" }
1248 }
1249 })";
1250 auto json = CreateDictionaryValue(kCommandDefs1);
1251 EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1252 const char kExpected1[] = R"({
1253 "package1": {
1254 "commands": {
1255 "command1": {
1256 "minimalRole": "user",
1257 "parameters": {"height": {"type": "integer"}}
1258 },
1259 "command2": {
1260 "minimalRole": "owner",
1261 "parameters": {}
1262 }
1263 }
1264 },
1265 "package2": {
1266 "commands": {
1267 "command1": { "minimalRole": "user" },
1268 "command2": { "minimalRole": "owner" }
1269 }
1270 }
1271 })";
1272 EXPECT_JSON_EQ(kExpected1, manager.GetTraits());
1273 const char kExpectedComponents1[] = R"({
1274 "__weave__": { "traits": ["package1", "package2"] }
1275 })";
1276 EXPECT_JSON_EQ(kExpectedComponents1, manager.GetComponents());
1277
1278 const char kCommandDefs2[] = R"({
1279 "package2": {
1280 "command3": { "minimalRole": "user" }
1281 },
1282 "package3": {
1283 "command1": { "minimalRole": "user" },
1284 "command2": { "minimalRole": "owner" }
1285 }
1286 })";
1287 json = CreateDictionaryValue(kCommandDefs2);
1288 EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1289 const char kExpected2[] = R"({
1290 "package1": {
1291 "commands": {
1292 "command1": {
1293 "minimalRole": "user",
1294 "parameters": {"height": {"type": "integer"}}
1295 },
1296 "command2": {
1297 "minimalRole": "owner",
1298 "parameters": {}
1299 }
1300 }
1301 },
1302 "package2": {
1303 "commands": {
1304 "command1": { "minimalRole": "user" },
1305 "command2": { "minimalRole": "owner" },
1306 "command3": { "minimalRole": "user" }
1307 }
1308 },
1309 "package3": {
1310 "commands": {
1311 "command1": { "minimalRole": "user" },
1312 "command2": { "minimalRole": "owner" }
1313 }
1314 }
1315 })";
1316 EXPECT_JSON_EQ(kExpected2, manager.GetTraits());
1317 const char kExpectedComponents2[] = R"({
1318 "__weave__": { "traits": ["package1", "package2", "package3"] }
1319 })";
1320 EXPECT_JSON_EQ(kExpectedComponents2, manager.GetComponents());
1321
1322 // Redefining existing commands.
1323 EXPECT_FALSE(manager.AddLegacyCommandDefinitions(*json, nullptr));
1324
1325 const char kStateDefs1[] = R"({
1326 "package1": {
1327 "prop1": { "type": "string" },
1328 "prop2": { "type": "string" }
1329 },
1330 "package4": {
1331 "prop3": { "type": "string" },
1332 "prop4": { "type": "string" }
1333 }
1334 })";
1335 json = CreateDictionaryValue(kStateDefs1);
1336 EXPECT_TRUE(manager.AddLegacyStateDefinitions(*json, nullptr));
1337 const char kExpectedComponents3[] = R"({
1338 "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1339 })";
1340 EXPECT_JSON_EQ(kExpectedComponents3, manager.GetComponents());
1341
1342 const char kExpected3[] = R"({
1343 "package1": {
1344 "commands": {
1345 "command1": {
1346 "minimalRole": "user",
1347 "parameters": {"height": {"type": "integer"}}
1348 },
1349 "command2": {
1350 "minimalRole": "owner",
1351 "parameters": {}
1352 }
1353 },
1354 "state": {
1355 "prop1": { "type": "string" },
1356 "prop2": { "type": "string" }
1357 }
1358 },
1359 "package2": {
1360 "commands": {
1361 "command1": { "minimalRole": "user" },
1362 "command2": { "minimalRole": "owner" },
1363 "command3": { "minimalRole": "user" }
1364 }
1365 },
1366 "package3": {
1367 "commands": {
1368 "command1": { "minimalRole": "user" },
1369 "command2": { "minimalRole": "owner" }
1370 }
1371 },
1372 "package4": {
1373 "state": {
1374 "prop3": { "type": "string" },
1375 "prop4": { "type": "string" }
1376 }
1377 }
1378 })";
1379 EXPECT_JSON_EQ(kExpected3, manager.GetTraits());
1380 const char kExpectedComponents4[] = R"({
1381 "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
1382 })";
1383 EXPECT_JSON_EQ(kExpectedComponents4, manager.GetComponents());
1384
1385 // Redefining existing commands.
1386 EXPECT_FALSE(manager.AddLegacyStateDefinitions(*json, nullptr));
1387
1388 const char kExpected4[] = R"({
1389 "package1": {
1390 "command1": {
1391 "minimalRole": "user",
1392 "parameters": {"height": {"type": "integer"}}
1393 },
1394 "command2": {
1395 "minimalRole": "owner",
1396 "parameters": {}
1397 }
1398 },
1399 "package2": {
1400 "command1": { "minimalRole": "user" },
1401 "command2": { "minimalRole": "owner" },
1402 "command3": { "minimalRole": "user" }
1403 },
1404 "package3": {
1405 "command1": { "minimalRole": "user" },
1406 "command2": { "minimalRole": "owner" }
1407 }
1408 })";
1409 EXPECT_JSON_EQ(kExpected4, manager.GetLegacyCommandDefinitions());
1410}
1411
1412TEST(ComponentManager, GetLegacyState) {
1413 ComponentManagerImpl manager;
1414 const char kTraits[] = R"({
1415 "trait1": {
1416 "state": {
1417 "prop1": { "type": "string" },
1418 "prop2": { "type": "string" }
1419 }
1420 },
1421 "trait2": {
1422 "state": {
1423 "prop3": { "type": "string" },
1424 "prop4": { "type": "string" }
1425 }
1426 }
1427 })";
1428 auto traits = CreateDictionaryValue(kTraits);
1429 ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
1430 ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
1431 ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
1432
1433 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
1434 "comp1", R"({"trait1": {"prop1": "foo", "prop2": "bar"}})", nullptr));
1435 ASSERT_TRUE(manager.SetStatePropertiesFromJson(
1436 "comp2", R"({"trait2": {"prop3": "baz", "prop4": "quux"}})", nullptr));
1437
1438 const char kExpected[] = R"({
1439 "trait1": {
1440 "prop1": "foo",
1441 "prop2": "bar"
1442 },
1443 "trait2": {
1444 "prop3": "baz",
1445 "prop4": "quux"
1446 }
1447 })";
1448 EXPECT_JSON_EQ(kExpected, manager.GetLegacyState());
1449}
1450
Alex Vakulenkoba981152015-12-05 13:58:22 -08001451TEST(ComponentManager, TestMockComponentManager) {
1452 // Check that all the virtual methods are mocked out.
1453 MockComponentManager mock;
1454}
1455
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08001456} // namespace weave