1 /**
2 * Copyright © 2024 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "chassis.hpp"
17 #include "config_file_parser.hpp"
18 #include "config_file_parser_error.hpp"
19 #include "mock_services.hpp"
20 #include "power_sequencer_device.hpp"
21 #include "rail.hpp"
22 #include "temporary_file.hpp"
23 #include "temporary_subdirectory.hpp"
24
25 #include <sys/stat.h> // for chmod()
26
27 #include <nlohmann/json.hpp>
28
29 #include <cstdint>
30 #include <exception>
31 #include <filesystem>
32 #include <fstream>
33 #include <map>
34 #include <memory>
35 #include <optional>
36 #include <stdexcept>
37 #include <string>
38 #include <tuple>
39 #include <vector>
40
41 #include <gtest/gtest.h>
42
43 using namespace phosphor::power::sequencer;
44 using namespace phosphor::power::sequencer::config_file_parser;
45 using namespace phosphor::power::sequencer::config_file_parser::internal;
46 using namespace phosphor::power::util;
47 using json = nlohmann::json;
48 namespace fs = std::filesystem;
49
writeConfigFile(const fs::path & pathName,const std::string & contents)50 void writeConfigFile(const fs::path& pathName, const std::string& contents)
51 {
52 std::ofstream file{pathName};
53 file << contents;
54 }
55
writeConfigFile(const fs::path & pathName,const json & contents)56 void writeConfigFile(const fs::path& pathName, const json& contents)
57 {
58 std::ofstream file{pathName};
59 file << contents;
60 }
61
TEST(ConfigFileParserTests,Find)62 TEST(ConfigFileParserTests, Find)
63 {
64 std::vector<std::string> compatibleSystemTypes{
65 "com.acme.Hardware.Chassis.Model.MegaServer4CPU",
66 "com.acme.Hardware.Chassis.Model.MegaServer",
67 "com.acme.Hardware.Chassis.Model.Server"};
68
69 // Test where works: Fully qualified system type: First in list
70 {
71 TemporarySubDirectory configFileDir;
72 fs::path configFileDirPath = configFileDir.getPath();
73
74 fs::path configFilePath = configFileDirPath;
75 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
76 writeConfigFile(configFilePath, std::string{""});
77
78 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
79 EXPECT_EQ(pathFound, configFilePath);
80 }
81
82 // Test where works: Fully qualified system type: Second in list
83 {
84 TemporarySubDirectory configFileDir;
85 fs::path configFileDirPath = configFileDir.getPath();
86
87 fs::path configFilePath = configFileDirPath;
88 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer.json";
89 writeConfigFile(configFilePath, std::string{""});
90
91 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
92 EXPECT_EQ(pathFound, configFilePath);
93 }
94
95 // Test where works: Last node in system type: Second in list
96 {
97 TemporarySubDirectory configFileDir;
98 fs::path configFileDirPath = configFileDir.getPath();
99
100 fs::path configFilePath = configFileDirPath;
101 configFilePath /= "MegaServer.json";
102 writeConfigFile(configFilePath, std::string{""});
103
104 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
105 EXPECT_EQ(pathFound, configFilePath);
106 }
107
108 // Test where works: Last node in system type: Last in list
109 {
110 TemporarySubDirectory configFileDir;
111 fs::path configFileDirPath = configFileDir.getPath();
112
113 fs::path configFilePath = configFileDirPath;
114 configFilePath /= "Server.json";
115 writeConfigFile(configFilePath, std::string{""});
116
117 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
118 EXPECT_EQ(pathFound, configFilePath);
119 }
120
121 // Test where works: System type has no '.'
122 {
123 TemporarySubDirectory configFileDir;
124 fs::path configFileDirPath = configFileDir.getPath();
125
126 fs::path configFilePath = configFileDirPath;
127 configFilePath /= "Server.json";
128 writeConfigFile(configFilePath, std::string{""});
129
130 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
131 "MegaServer", "Server"};
132 fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
133 EXPECT_EQ(pathFound, configFilePath);
134 }
135
136 // Test where fails: System type list is empty
137 {
138 TemporarySubDirectory configFileDir;
139 fs::path configFileDirPath = configFileDir.getPath();
140
141 fs::path configFilePath = configFileDirPath;
142 configFilePath /= "Server.json";
143 writeConfigFile(configFilePath, std::string{""});
144
145 std::vector<std::string> emptySystemTypes{};
146 fs::path pathFound = find(emptySystemTypes, configFileDirPath);
147 EXPECT_TRUE(pathFound.empty());
148 }
149
150 // Test where fails: Configuration file directory is empty
151 {
152 TemporarySubDirectory configFileDir;
153 fs::path configFileDirPath = configFileDir.getPath();
154
155 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
156 EXPECT_TRUE(pathFound.empty());
157 }
158
159 // Test where fails: Configuration file directory does not exist
160 {
161 fs::path configFileDirPath{"/tmp/does_not_exist_XYZ"};
162
163 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
164 EXPECT_TRUE(pathFound.empty());
165 }
166
167 // Test where fails: Configuration file directory is not readable
168 {
169 TemporarySubDirectory configFileDir;
170 fs::path configFileDirPath = configFileDir.getPath();
171 fs::permissions(configFileDirPath, fs::perms::none);
172
173 EXPECT_THROW(find(compatibleSystemTypes, configFileDirPath),
174 std::exception);
175
176 fs::permissions(configFileDirPath, fs::perms::owner_all);
177 }
178
179 // Test where fails: No matching file name found
180 {
181 TemporarySubDirectory configFileDir;
182 fs::path configFileDirPath = configFileDir.getPath();
183
184 fs::path configFilePath = configFileDirPath;
185 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer";
186 writeConfigFile(configFilePath, std::string{""});
187
188 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
189 EXPECT_TRUE(pathFound.empty());
190 }
191
192 // Test where fails: Matching file name is a directory: Fully qualified
193 {
194 TemporarySubDirectory configFileDir;
195 fs::path configFileDirPath = configFileDir.getPath();
196
197 fs::path configFilePath = configFileDirPath;
198 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
199 fs::create_directory(configFilePath);
200
201 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
202 EXPECT_TRUE(pathFound.empty());
203 }
204
205 // Test where fails: Matching file name is a directory: Last node
206 {
207 TemporarySubDirectory configFileDir;
208 fs::path configFileDirPath = configFileDir.getPath();
209
210 fs::path configFilePath = configFileDirPath;
211 configFilePath /= "MegaServer.json";
212 fs::create_directory(configFilePath);
213
214 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
215 EXPECT_TRUE(pathFound.empty());
216 }
217
218 // Test where fails: System type has no '.'
219 {
220 TemporarySubDirectory configFileDir;
221 fs::path configFileDirPath = configFileDir.getPath();
222
223 fs::path configFilePath = configFileDirPath;
224 configFilePath /= "MegaServer2CPU.json";
225 writeConfigFile(configFilePath, std::string{""});
226
227 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
228 "MegaServer", "Server", ""};
229 fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
230 EXPECT_TRUE(pathFound.empty());
231 }
232
233 // Test where fails: System type ends with '.'
234 {
235 TemporarySubDirectory configFileDir;
236 fs::path configFileDirPath = configFileDir.getPath();
237
238 fs::path configFilePath = configFileDirPath;
239 configFilePath /= "MegaServer4CPU.json";
240 writeConfigFile(configFilePath, std::string{""});
241
242 std::vector<std::string> dotAtEndSystemTypes{
243 "com.acme.Hardware.Chassis.Model.MegaServer4CPU.", "a.", "."};
244 fs::path pathFound = find(dotAtEndSystemTypes, configFileDirPath);
245 EXPECT_TRUE(pathFound.empty());
246 }
247 }
248
TEST(ConfigFileParserTests,Parse)249 TEST(ConfigFileParserTests, Parse)
250 {
251 // Test where works
252 {
253 const json configFileContents = R"(
254 {
255 "chassis": [
256 {
257 "number": 1,
258 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
259 "power_sequencers": []
260 },
261 {
262 "number": 2,
263 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
264 "power_sequencers": []
265 }
266 ]
267 }
268 )"_json;
269
270 TemporaryFile configFile;
271 fs::path pathName{configFile.getPath()};
272 writeConfigFile(pathName, configFileContents);
273
274 MockServices services{};
275 auto chassis = parse(pathName, services);
276
277 EXPECT_EQ(chassis.size(), 2);
278 EXPECT_EQ(chassis[0]->getNumber(), 1);
279 EXPECT_EQ(chassis[0]->getInventoryPath(),
280 "/xyz/openbmc_project/inventory/system/chassis1");
281 EXPECT_EQ(chassis[1]->getNumber(), 2);
282 EXPECT_EQ(chassis[1]->getInventoryPath(),
283 "/xyz/openbmc_project/inventory/system/chassis2");
284 }
285
286 // Test where fails: File does not exist
287 {
288 fs::path pathName{"/tmp/non_existent_file"};
289 MockServices services{};
290 EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
291 }
292
293 // Test where fails: File is not readable
294 {
295 const json configFileContents = R"(
296 {
297 "chassis": [
298 {
299 "number": 1,
300 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
301 "power_sequencers": []
302 }
303 ]
304 }
305 )"_json;
306
307 TemporaryFile configFile;
308 fs::path pathName{configFile.getPath()};
309 writeConfigFile(pathName, configFileContents);
310
311 chmod(pathName.c_str(), 0222);
312 MockServices services{};
313 EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
314 }
315
316 // Test where fails: File is not valid JSON
317 {
318 const std::string configFileContents = "] foo [";
319
320 TemporaryFile configFile;
321 fs::path pathName{configFile.getPath()};
322 writeConfigFile(pathName, configFileContents);
323
324 MockServices services{};
325 EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
326 }
327
328 // Test where fails: JSON does not conform to config file format
329 {
330 const json configFileContents = R"( [ "foo", "bar" ] )"_json;
331
332 TemporaryFile configFile;
333 fs::path pathName{configFile.getPath()};
334 writeConfigFile(pathName, configFileContents);
335
336 MockServices services{};
337 EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
338 }
339 }
340
TEST(ConfigFileParserTests,ParseChassis)341 TEST(ConfigFileParserTests, ParseChassis)
342 {
343 // Constants used by multiple tests
344 const json templateElement = R"(
345 {
346 "id": "foo_chassis",
347 "number": "${chassis_number}",
348 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
349 "power_sequencers": [
350 {
351 "type": "UCD90320",
352 "i2c_interface": { "bus": "${bus}", "address": "${address}" },
353 "power_control_gpio_name": "power-chassis${chassis_number}-control",
354 "power_good_gpio_name": "power-chassis${chassis_number}-good",
355 "rails": []
356 }
357 ]
358 }
359 )"_json;
360 const std::map<std::string, JSONRefWrapper> chassisTemplates{
361 {"foo_chassis", templateElement}};
362
363 // Test where works: Does not use a template
364 {
365 const json element = R"(
366 {
367 "number": 1,
368 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
369 "power_sequencers": [
370 {
371 "type": "UCD90320",
372 "i2c_interface": { "bus": 3, "address": "0x11" },
373 "power_control_gpio_name": "power-chassis-control",
374 "power_good_gpio_name": "power-chassis-good",
375 "rails": []
376 }
377 ]
378 }
379 )"_json;
380 MockServices services{};
381 auto chassis = parseChassis(element, chassisTemplates, services);
382 EXPECT_EQ(chassis->getNumber(), 1);
383 EXPECT_EQ(chassis->getInventoryPath(),
384 "/xyz/openbmc_project/inventory/system/chassis");
385 EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
386 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
387 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3);
388 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11);
389 }
390
391 // Test where works: Uses template: No comments specified
392 {
393 const json element = R"(
394 {
395 "template_id": "foo_chassis",
396 "template_variable_values": {
397 "chassis_number": "2",
398 "bus": "13",
399 "address": "0x70"
400 }
401 }
402 )"_json;
403 MockServices services{};
404 auto chassis = parseChassis(element, chassisTemplates, services);
405 EXPECT_EQ(chassis->getNumber(), 2);
406 EXPECT_EQ(chassis->getInventoryPath(),
407 "/xyz/openbmc_project/inventory/system/chassis2");
408 EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
409 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
410 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 13);
411 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x70);
412 }
413
414 // Test where works: Uses template: Comments specified
415 {
416 const json element = R"(
417 {
418 "comments": ["Chassis 3: Standard hardware layout"],
419 "template_id": "foo_chassis",
420 "template_variable_values": {
421 "chassis_number": "3",
422 "bus": "23",
423 "address": "0x54"
424 }
425 }
426 )"_json;
427 MockServices services{};
428 auto chassis = parseChassis(element, chassisTemplates, services);
429 EXPECT_EQ(chassis->getNumber(), 3);
430 EXPECT_EQ(chassis->getInventoryPath(),
431 "/xyz/openbmc_project/inventory/system/chassis3");
432 EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
433 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
434 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 23);
435 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x54);
436 }
437
438 // Test where fails: Element is not an object
439 try
440 {
441 const json element = R"( [ "vdda", "vddb" ] )"_json;
442 MockServices services{};
443 parseChassis(element, chassisTemplates, services);
444 ADD_FAILURE() << "Should not have reached this line.";
445 }
446 catch (const std::invalid_argument& e)
447 {
448 EXPECT_STREQ(e.what(), "Element is not an object");
449 }
450
451 // Test where fails: Does not use a template: Cannot parse properties
452 try
453 {
454 const json element = R"(
455 {
456 "number": "one",
457 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
458 "power_sequencers": []
459 }
460 )"_json;
461 MockServices services{};
462 parseChassis(element, chassisTemplates, services);
463 ADD_FAILURE() << "Should not have reached this line.";
464 }
465 catch (const std::invalid_argument& e)
466 {
467 EXPECT_STREQ(e.what(), "Element is not an integer");
468 }
469
470 // Test where fails: Uses template: Required template_variable_values
471 // property not specified
472 try
473 {
474 const json element = R"(
475 {
476 "template_id": "foo_chassis"
477 }
478 )"_json;
479 MockServices services{};
480 parseChassis(element, chassisTemplates, services);
481 ADD_FAILURE() << "Should not have reached this line.";
482 }
483 catch (const std::invalid_argument& e)
484 {
485 EXPECT_STREQ(e.what(),
486 "Required property missing: template_variable_values");
487 }
488
489 // Test where fails: Uses template: template_id value is invalid: Not a
490 // string
491 try
492 {
493 const json element = R"(
494 {
495 "template_id": 23,
496 "template_variable_values": { "chassis_number": "2" }
497 }
498 )"_json;
499 MockServices services{};
500 parseChassis(element, chassisTemplates, services);
501 ADD_FAILURE() << "Should not have reached this line.";
502 }
503 catch (const std::invalid_argument& e)
504 {
505 EXPECT_STREQ(e.what(), "Element is not a string");
506 }
507
508 // Test where fails: Uses template: template_id value is invalid: No
509 // matching template
510 try
511 {
512 const json element = R"(
513 {
514 "template_id": "does_not_exist",
515 "template_variable_values": { "chassis_number": "2" }
516 }
517 )"_json;
518 MockServices services{};
519 parseChassis(element, chassisTemplates, services);
520 ADD_FAILURE() << "Should not have reached this line.";
521 }
522 catch (const std::invalid_argument& e)
523 {
524 EXPECT_STREQ(e.what(), "Invalid chassis template id: does_not_exist");
525 }
526
527 // Test where fails: Uses template: template_variable_values value is
528 // invalid
529 try
530 {
531 const json element = R"(
532 {
533 "template_id": "foo_chassis",
534 "template_variable_values": { "chassis_number": 2 }
535 }
536 )"_json;
537 MockServices services{};
538 parseChassis(element, chassisTemplates, services);
539 ADD_FAILURE() << "Should not have reached this line.";
540 }
541 catch (const std::invalid_argument& e)
542 {
543 EXPECT_STREQ(e.what(), "Element is not a string");
544 }
545
546 // Test where fails: Uses template: Invalid property specified
547 try
548 {
549 const json element = R"(
550 {
551 "template_id": "foo_chassis",
552 "template_variable_values": { "chassis_number": "2" },
553 "foo": "bar"
554 }
555 )"_json;
556 MockServices services{};
557 parseChassis(element, chassisTemplates, services);
558 ADD_FAILURE() << "Should not have reached this line.";
559 }
560 catch (const std::invalid_argument& e)
561 {
562 EXPECT_STREQ(e.what(), "Element contains an invalid property");
563 }
564
565 // Test where fails: Uses template: Cannot parse properties in template
566 try
567 {
568 const json element = R"(
569 {
570 "template_id": "foo_chassis",
571 "template_variable_values": { "chassis_number": "0" }
572 }
573 )"_json;
574 MockServices services{};
575 parseChassis(element, chassisTemplates, services);
576 ADD_FAILURE() << "Should not have reached this line.";
577 }
578 catch (const std::invalid_argument& e)
579 {
580 EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0");
581 }
582 }
583
TEST(ConfigFileParserTests,ParseChassisArray)584 TEST(ConfigFileParserTests, ParseChassisArray)
585 {
586 // Constants used by multiple tests
587 const json fooTemplateElement = R"(
588 {
589 "id": "foo_chassis",
590 "number": "${chassis_number}",
591 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
592 "power_sequencers": []
593 }
594 )"_json;
595 const json barTemplateElement = R"(
596 {
597 "id": "bar_chassis",
598 "number": "${chassis_number}",
599 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}",
600 "power_sequencers": []
601 }
602 )"_json;
603 const std::map<std::string, JSONRefWrapper> chassisTemplates{
604 {"foo_chassis", fooTemplateElement},
605 {"bar_chassis", barTemplateElement}};
606
607 // Test where works: Array is empty
608 {
609 const json element = R"(
610 [
611 ]
612 )"_json;
613 MockServices services{};
614 auto chassis = parseChassisArray(element, chassisTemplates, services);
615 EXPECT_EQ(chassis.size(), 0);
616 }
617
618 // Test where works: Template not used
619 {
620 const json element = R"(
621 [
622 {
623 "number": 1,
624 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
625 "power_sequencers": []
626 },
627 {
628 "number": 2,
629 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
630 "power_sequencers": []
631 }
632 ]
633 )"_json;
634 MockServices services{};
635 auto chassis = parseChassisArray(element, chassisTemplates, services);
636 EXPECT_EQ(chassis.size(), 2);
637 EXPECT_EQ(chassis[0]->getNumber(), 1);
638 EXPECT_EQ(chassis[0]->getInventoryPath(),
639 "/xyz/openbmc_project/inventory/system/chassis1");
640 EXPECT_EQ(chassis[1]->getNumber(), 2);
641 EXPECT_EQ(chassis[1]->getInventoryPath(),
642 "/xyz/openbmc_project/inventory/system/chassis2");
643 }
644
645 // Test where works: Template used
646 {
647 const json element = R"(
648 [
649 {
650 "template_id": "foo_chassis",
651 "template_variable_values": { "chassis_number": "2" }
652 },
653 {
654 "template_id": "bar_chassis",
655 "template_variable_values": { "chassis_number": "3" }
656 }
657 ]
658 )"_json;
659 MockServices services{};
660 auto chassis = parseChassisArray(element, chassisTemplates, services);
661 EXPECT_EQ(chassis.size(), 2);
662 EXPECT_EQ(chassis[0]->getNumber(), 2);
663 EXPECT_EQ(chassis[0]->getInventoryPath(),
664 "/xyz/openbmc_project/inventory/system/chassis2");
665 EXPECT_EQ(chassis[1]->getNumber(), 3);
666 EXPECT_EQ(chassis[1]->getInventoryPath(),
667 "/xyz/openbmc_project/inventory/system/bar_chassis3");
668 }
669
670 // Test where fails: Element is not an array
671 try
672 {
673 const json element = R"(
674 {
675 "foo": "bar"
676 }
677 )"_json;
678 MockServices services{};
679 parseChassisArray(element, chassisTemplates, services);
680 ADD_FAILURE() << "Should not have reached this line.";
681 }
682 catch (const std::invalid_argument& e)
683 {
684 EXPECT_STREQ(e.what(), "Element is not an array");
685 }
686
687 // Test where fails: Element within array is invalid
688 try
689 {
690 const json element = R"(
691 [
692 {
693 "number": true,
694 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
695 "power_sequencers": []
696 }
697 ]
698 )"_json;
699 MockServices services{};
700 parseChassisArray(element, chassisTemplates, services);
701 ADD_FAILURE() << "Should not have reached this line.";
702 }
703 catch (const std::invalid_argument& e)
704 {
705 EXPECT_STREQ(e.what(), "Element is not an integer");
706 }
707
708 // Test where fails: Invalid variable value specified
709 try
710 {
711 const json element = R"(
712 [
713 {
714 "template_id": "foo_chassis",
715 "template_variable_values": { "chassis_number": "two" }
716 }
717 ]
718 )"_json;
719 MockServices services{};
720 parseChassisArray(element, chassisTemplates, services);
721 ADD_FAILURE() << "Should not have reached this line.";
722 }
723 catch (const std::invalid_argument& e)
724 {
725 EXPECT_STREQ(e.what(), "Element is not an integer");
726 }
727 }
728
TEST(ConfigFileParserTests,ParseChassisProperties)729 TEST(ConfigFileParserTests, ParseChassisProperties)
730 {
731 // Test where works: Parse chassis object without template/variables: Has
732 // comments property
733 {
734 const json element = R"(
735 {
736 "comments": [ "Chassis 1: Has all CPUs, fans, and PSUs" ],
737 "number": 1,
738 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
739 "power_sequencers": [
740 {
741 "type": "UCD90160",
742 "i2c_interface": { "bus": 3, "address": "0x11" },
743 "power_control_gpio_name": "power-chassis-control",
744 "power_good_gpio_name": "power-chassis-good",
745 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ]
746 }
747 ]
748 }
749 )"_json;
750 bool isChassisTemplate{false};
751 std::map<std::string, std::string> variables{};
752 MockServices services{};
753 auto chassis = parseChassisProperties(element, isChassisTemplate,
754 variables, services);
755 EXPECT_EQ(chassis->getNumber(), 1);
756 EXPECT_EQ(chassis->getInventoryPath(),
757 "/xyz/openbmc_project/inventory/system/chassis");
758 EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
759 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90160");
760 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3);
761 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11);
762 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(),
763 "power-chassis-control");
764 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(),
765 "power-chassis-good");
766 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 2);
767 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(),
768 "VDD_CPU0");
769 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[1]->getName(),
770 "VCS_CPU1");
771 }
772
773 // Test where works: Parse chassis_template object with variables: No
774 // comments property
775 {
776 const json element = R"(
777 {
778 "id": "foo_chassis",
779 "number": "${chassis_number}",
780 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
781 "power_sequencers": [
782 {
783 "type": "UCD90320",
784 "i2c_interface": { "bus": "${bus}", "address": "${address}" },
785 "power_control_gpio_name": "power-chassis${chassis_number}-control",
786 "power_good_gpio_name": "power-chassis${chassis_number}-good",
787 "rails": [ { "name": "vio${chassis_number}" } ]
788 }
789 ]
790 }
791 )"_json;
792 bool isChassisTemplate{true};
793 std::map<std::string, std::string> variables{
794 {"chassis_number", "2"}, {"bus", "12"}, {"address", "0x71"}};
795 MockServices services{};
796 auto chassis = parseChassisProperties(element, isChassisTemplate,
797 variables, services);
798 EXPECT_EQ(chassis->getNumber(), 2);
799 EXPECT_EQ(chassis->getInventoryPath(),
800 "/xyz/openbmc_project/inventory/system/chassis2");
801 EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
802 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
803 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 12);
804 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x71);
805 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(),
806 "power-chassis2-control");
807 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(),
808 "power-chassis2-good");
809 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 1);
810 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(),
811 "vio2");
812 }
813
814 // Test where fails: Element is not an object
815 try
816 {
817 const json element = R"( true )"_json;
818 bool isChassisTemplate{false};
819 std::map<std::string, std::string> variables{};
820 MockServices services{};
821 parseChassisProperties(element, isChassisTemplate, variables, services);
822 ADD_FAILURE() << "Should not have reached this line.";
823 }
824 catch (const std::invalid_argument& e)
825 {
826 EXPECT_STREQ(e.what(), "Element is not an object");
827 }
828
829 // Test where fails: Required id property not specified in chassis template
830 try
831 {
832 const json element = R"(
833 {
834 "number": "${chassis_number}",
835 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
836 "power_sequencers": []
837 }
838 )"_json;
839 bool isChassisTemplate{true};
840 std::map<std::string, std::string> variables{{"chassis_number", "2"}};
841 MockServices services{};
842 parseChassisProperties(element, isChassisTemplate, variables, services);
843 ADD_FAILURE() << "Should not have reached this line.";
844 }
845 catch (const std::invalid_argument& e)
846 {
847 EXPECT_STREQ(e.what(), "Required property missing: id");
848 }
849
850 // Test where fails: Required number property not specified
851 try
852 {
853 const json element = R"(
854 {
855 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
856 "power_sequencers": []
857 }
858 )"_json;
859 bool isChassisTemplate{false};
860 std::map<std::string, std::string> variables{};
861 MockServices services{};
862 parseChassisProperties(element, isChassisTemplate, variables, services);
863 ADD_FAILURE() << "Should not have reached this line.";
864 }
865 catch (const std::invalid_argument& e)
866 {
867 EXPECT_STREQ(e.what(), "Required property missing: number");
868 }
869
870 // Test where fails: Required inventory_path property not specified
871 try
872 {
873 const json element = R"(
874 {
875 "id": "foo_chassis",
876 "number": "${chassis_number}",
877 "power_sequencers": []
878 }
879 )"_json;
880 bool isChassisTemplate{true};
881 std::map<std::string, std::string> variables{{"chassis_number", "2"}};
882 MockServices services{};
883 parseChassisProperties(element, isChassisTemplate, variables, services);
884 ADD_FAILURE() << "Should not have reached this line.";
885 }
886 catch (const std::invalid_argument& e)
887 {
888 EXPECT_STREQ(e.what(), "Required property missing: inventory_path");
889 }
890
891 // Test where fails: Required power_sequencers property not specified
892 try
893 {
894 const json element = R"(
895 {
896 "number": 1,
897 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis"
898 }
899 )"_json;
900 bool isChassisTemplate{false};
901 std::map<std::string, std::string> variables{};
902 MockServices services{};
903 parseChassisProperties(element, isChassisTemplate, variables, services);
904 ADD_FAILURE() << "Should not have reached this line.";
905 }
906 catch (const std::invalid_argument& e)
907 {
908 EXPECT_STREQ(e.what(), "Required property missing: power_sequencers");
909 }
910
911 // Test where fails: number value is invalid: Not an integer
912 try
913 {
914 const json element = R"(
915 {
916 "number": "two",
917 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
918 "power_sequencers": []
919 }
920 )"_json;
921 bool isChassisTemplate{false};
922 std::map<std::string, std::string> variables{};
923 MockServices services{};
924 parseChassisProperties(element, isChassisTemplate, variables, services);
925 ADD_FAILURE() << "Should not have reached this line.";
926 }
927 catch (const std::invalid_argument& e)
928 {
929 EXPECT_STREQ(e.what(), "Element is not an integer");
930 }
931
932 // Test where fails: number value is invalid: Equal to 0
933 try
934 {
935 const json element = R"(
936 {
937 "number": 0,
938 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
939 "power_sequencers": []
940 }
941 )"_json;
942 bool isChassisTemplate{false};
943 std::map<std::string, std::string> variables{};
944 MockServices services{};
945 parseChassisProperties(element, isChassisTemplate, variables, services);
946 ADD_FAILURE() << "Should not have reached this line.";
947 }
948 catch (const std::invalid_argument& e)
949 {
950 EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0");
951 }
952
953 // Test where fails: inventory_path value is invalid
954 try
955 {
956 const json element = R"(
957 {
958 "number": 1,
959 "inventory_path": "",
960 "power_sequencers": []
961 }
962 )"_json;
963 bool isChassisTemplate{false};
964 std::map<std::string, std::string> variables{};
965 MockServices services{};
966 parseChassisProperties(element, isChassisTemplate, variables, services);
967 ADD_FAILURE() << "Should not have reached this line.";
968 }
969 catch (const std::invalid_argument& e)
970 {
971 EXPECT_STREQ(e.what(), "Element contains an empty string");
972 }
973
974 // Test where fails: power_sequencers value is invalid
975 try
976 {
977 const json element = R"(
978 {
979 "number": 1,
980 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
981 "power_sequencers": { "name": "foo" }
982 }
983 )"_json;
984 bool isChassisTemplate{false};
985 std::map<std::string, std::string> variables{};
986 MockServices services{};
987 parseChassisProperties(element, isChassisTemplate, variables, services);
988 ADD_FAILURE() << "Should not have reached this line.";
989 }
990 catch (const std::invalid_argument& e)
991 {
992 EXPECT_STREQ(e.what(), "Element is not an array");
993 }
994
995 // Test where fails: Invalid property specified
996 try
997 {
998 const json element = R"(
999 {
1000 "foo": "bar",
1001 "number": 1,
1002 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
1003 "power_sequencers": []
1004 }
1005 )"_json;
1006 bool isChassisTemplate{false};
1007 std::map<std::string, std::string> variables{};
1008 MockServices services{};
1009 parseChassisProperties(element, isChassisTemplate, variables, services);
1010 ADD_FAILURE() << "Should not have reached this line.";
1011 }
1012 catch (const std::invalid_argument& e)
1013 {
1014 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1015 }
1016
1017 // Test where fails: Invalid variable value specified
1018 try
1019 {
1020 const json element = R"(
1021 {
1022 "id": "foo_chassis",
1023 "number": "${chassis_number}",
1024 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1025 "power_sequencers": []
1026 }
1027 )"_json;
1028 bool isChassisTemplate{true};
1029 std::map<std::string, std::string> variables{{"chassis_number", "two"}};
1030 MockServices services{};
1031 parseChassisProperties(element, isChassisTemplate, variables, services);
1032 ADD_FAILURE() << "Should not have reached this line.";
1033 }
1034 catch (const std::invalid_argument& e)
1035 {
1036 EXPECT_STREQ(e.what(), "Element is not an integer");
1037 }
1038 }
1039
TEST(ConfigFileParserTests,ParseChassisTemplate)1040 TEST(ConfigFileParserTests, ParseChassisTemplate)
1041 {
1042 // Test where works: comments specified
1043 {
1044 const json element = R"(
1045 {
1046 "comments": [ "This is a template for the foo chassis type",
1047 "Chassis contains a UCD90320 power sequencer" ],
1048 "id": "foo_chassis",
1049 "number": "${chassis_number}",
1050 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1051 "power_sequencers": [
1052 {
1053 "type": "UCD90320",
1054 "i2c_interface": { "bus": "${bus}", "address": "0x11" },
1055 "power_control_gpio_name": "power-chassis${chassis_number}-control",
1056 "power_good_gpio_name": "power-chassis${chassis_number}-good",
1057 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ]
1058 }
1059 ]
1060 }
1061 )"_json;
1062 auto [id, jsonRef] = parseChassisTemplate(element);
1063 EXPECT_EQ(id, "foo_chassis");
1064 EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}");
1065 EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 1);
1066 EXPECT_EQ(jsonRef.get()["power_sequencers"][0]["type"], "UCD90320");
1067 }
1068
1069 // Test where works: comments not specified
1070 {
1071 const json element = R"(
1072 {
1073 "id": "foo_chassis",
1074 "number": "${chassis_number}",
1075 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1076 "power_sequencers": []
1077 }
1078 )"_json;
1079 auto [id, jsonRef] = parseChassisTemplate(element);
1080 EXPECT_EQ(id, "foo_chassis");
1081 EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}");
1082 EXPECT_EQ(
1083 jsonRef.get()["inventory_path"],
1084 "/xyz/openbmc_project/inventory/system/chassis${chassis_number}");
1085 EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 0);
1086 }
1087
1088 // Test where fails: Element is not an object
1089 try
1090 {
1091 const json element = R"( [ "vdda", "vddb" ] )"_json;
1092 parseChassisTemplate(element);
1093 ADD_FAILURE() << "Should not have reached this line.";
1094 }
1095 catch (const std::invalid_argument& e)
1096 {
1097 EXPECT_STREQ(e.what(), "Element is not an object");
1098 }
1099
1100 // Test where fails: Required id property not specified
1101 try
1102 {
1103 const json element = R"(
1104 {
1105 "number": "${chassis_number}",
1106 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1107 "power_sequencers": []
1108 }
1109 )"_json;
1110 parseChassisTemplate(element);
1111 ADD_FAILURE() << "Should not have reached this line.";
1112 }
1113 catch (const std::invalid_argument& e)
1114 {
1115 EXPECT_STREQ(e.what(), "Required property missing: id");
1116 }
1117
1118 // Test where fails: Required number property not specified
1119 try
1120 {
1121 const json element = R"(
1122 {
1123 "id": "foo_chassis",
1124 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1125 "power_sequencers": []
1126 }
1127 )"_json;
1128 parseChassisTemplate(element);
1129 ADD_FAILURE() << "Should not have reached this line.";
1130 }
1131 catch (const std::invalid_argument& e)
1132 {
1133 EXPECT_STREQ(e.what(), "Required property missing: number");
1134 }
1135
1136 // Test where fails: Required inventory_path property not specified
1137 try
1138 {
1139 const json element = R"(
1140 {
1141 "id": "foo_chassis",
1142 "number": "${chassis_number}",
1143 "power_sequencers": []
1144 }
1145 )"_json;
1146 parseChassisTemplate(element);
1147 ADD_FAILURE() << "Should not have reached this line.";
1148 }
1149 catch (const std::invalid_argument& e)
1150 {
1151 EXPECT_STREQ(e.what(), "Required property missing: inventory_path");
1152 }
1153
1154 // Test where fails: Required power_sequencers property not specified
1155 try
1156 {
1157 const json element = R"(
1158 {
1159 "id": "foo_chassis",
1160 "number": "${chassis_number}",
1161 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}"
1162 }
1163 )"_json;
1164 parseChassisTemplate(element);
1165 ADD_FAILURE() << "Should not have reached this line.";
1166 }
1167 catch (const std::invalid_argument& e)
1168 {
1169 EXPECT_STREQ(e.what(), "Required property missing: power_sequencers");
1170 }
1171
1172 // Test where fails: id value is invalid
1173 try
1174 {
1175 const json element = R"(
1176 {
1177 "id": 13,
1178 "number": "${chassis_number}",
1179 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1180 "power_sequencers": []
1181 }
1182 )"_json;
1183 parseChassisTemplate(element);
1184 ADD_FAILURE() << "Should not have reached this line.";
1185 }
1186 catch (const std::invalid_argument& e)
1187 {
1188 EXPECT_STREQ(e.what(), "Element is not a string");
1189 }
1190
1191 // Test where fails: Invalid property specified
1192 try
1193 {
1194 const json element = R"(
1195 {
1196 "id": "foo_chassis",
1197 "number": "${chassis_number}",
1198 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1199 "power_sequencers": [],
1200 "foo": "bar"
1201 }
1202 )"_json;
1203 parseChassisTemplate(element);
1204 ADD_FAILURE() << "Should not have reached this line.";
1205 }
1206 catch (const std::invalid_argument& e)
1207 {
1208 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1209 }
1210 }
1211
TEST(ConfigFileParserTests,ParseChassisTemplateArray)1212 TEST(ConfigFileParserTests, ParseChassisTemplateArray)
1213 {
1214 // Test where works: Array is empty
1215 {
1216 const json element = R"(
1217 [
1218 ]
1219 )"_json;
1220 auto chassisTemplates = parseChassisTemplateArray(element);
1221 EXPECT_EQ(chassisTemplates.size(), 0);
1222 }
1223
1224 // Test where works: Array is not empty
1225 {
1226 const json element = R"(
1227 [
1228 {
1229 "id": "foo_chassis",
1230 "number": "${chassis_number}",
1231 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1232 "power_sequencers": []
1233 },
1234 {
1235 "id": "bar_chassis",
1236 "number": "${number}",
1237 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${number}",
1238 "power_sequencers": []
1239 }
1240 ]
1241 )"_json;
1242 auto chassisTemplates = parseChassisTemplateArray(element);
1243 EXPECT_EQ(chassisTemplates.size(), 2);
1244 EXPECT_EQ(chassisTemplates.at("foo_chassis").get()["number"],
1245 "${chassis_number}");
1246 EXPECT_EQ(
1247 chassisTemplates.at("foo_chassis").get()["inventory_path"],
1248 "/xyz/openbmc_project/inventory/system/chassis${chassis_number}");
1249 EXPECT_EQ(chassisTemplates.at("bar_chassis").get()["number"],
1250 "${number}");
1251 EXPECT_EQ(chassisTemplates.at("bar_chassis").get()["inventory_path"],
1252 "/xyz/openbmc_project/inventory/system/bar_chassis${number}");
1253 }
1254
1255 // Test where fails: Element is not an array
1256 try
1257 {
1258 const json element = R"(
1259 {
1260 "foo": "bar"
1261 }
1262 )"_json;
1263 parseChassisTemplateArray(element);
1264 ADD_FAILURE() << "Should not have reached this line.";
1265 }
1266 catch (const std::invalid_argument& e)
1267 {
1268 EXPECT_STREQ(e.what(), "Element is not an array");
1269 }
1270
1271 // Test where fails: Element within array is invalid
1272 try
1273 {
1274 const json element = R"(
1275 [
1276 {
1277 "id": "foo_chassis",
1278 "number": "${chassis_number}",
1279 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
1280 "power_sequencers": []
1281 },
1282 {
1283 "id": "bar_chassis"
1284 }
1285 ]
1286 )"_json;
1287 parseChassisTemplateArray(element);
1288 ADD_FAILURE() << "Should not have reached this line.";
1289 }
1290 catch (const std::invalid_argument& e)
1291 {
1292 EXPECT_STREQ(e.what(), "Required property missing: number");
1293 }
1294 }
1295
TEST(ConfigFileParserTests,ParseGPIO)1296 TEST(ConfigFileParserTests, ParseGPIO)
1297 {
1298 // Test where works: Only required properties specified
1299 {
1300 const json element = R"(
1301 {
1302 "line": 60
1303 }
1304 )"_json;
1305 std::map<std::string, std::string> variables{};
1306 auto gpio = parseGPIO(element, variables);
1307 EXPECT_EQ(gpio.line, 60);
1308 EXPECT_FALSE(gpio.activeLow);
1309 }
1310
1311 // Test where works: All properties specified
1312 {
1313 const json element = R"(
1314 {
1315 "line": 131,
1316 "active_low": true
1317 }
1318 )"_json;
1319 std::map<std::string, std::string> variables{};
1320 auto gpio = parseGPIO(element, variables);
1321 EXPECT_EQ(gpio.line, 131);
1322 EXPECT_TRUE(gpio.activeLow);
1323 }
1324
1325 // Test where works: Variables specified
1326 {
1327 const json element = R"(
1328 {
1329 "line": "${line}",
1330 "active_low": "${active_low}"
1331 }
1332 )"_json;
1333 std::map<std::string, std::string> variables{{"line", "54"},
1334 {"active_low", "false"}};
1335 auto gpio = parseGPIO(element, variables);
1336 EXPECT_EQ(gpio.line, 54);
1337 EXPECT_FALSE(gpio.activeLow);
1338 }
1339
1340 // Test where fails: Element is not an object
1341 try
1342 {
1343 const json element = R"( [ "vdda", "vddb" ] )"_json;
1344 std::map<std::string, std::string> variables{};
1345 parseGPIO(element, variables);
1346 ADD_FAILURE() << "Should not have reached this line.";
1347 }
1348 catch (const std::invalid_argument& e)
1349 {
1350 EXPECT_STREQ(e.what(), "Element is not an object");
1351 }
1352
1353 // Test where fails: Required line property not specified
1354 try
1355 {
1356 const json element = R"(
1357 {
1358 "active_low": true
1359 }
1360 )"_json;
1361 std::map<std::string, std::string> variables{};
1362 parseGPIO(element, variables);
1363 ADD_FAILURE() << "Should not have reached this line.";
1364 }
1365 catch (const std::invalid_argument& e)
1366 {
1367 EXPECT_STREQ(e.what(), "Required property missing: line");
1368 }
1369
1370 // Test where fails: line value is invalid
1371 try
1372 {
1373 const json element = R"(
1374 {
1375 "line": -131,
1376 "active_low": true
1377 }
1378 )"_json;
1379 std::map<std::string, std::string> variables{};
1380 parseGPIO(element, variables);
1381 ADD_FAILURE() << "Should not have reached this line.";
1382 }
1383 catch (const std::invalid_argument& e)
1384 {
1385 EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
1386 }
1387
1388 // Test where fails: active_low value is invalid
1389 try
1390 {
1391 const json element = R"(
1392 {
1393 "line": 131,
1394 "active_low": "true"
1395 }
1396 )"_json;
1397 std::map<std::string, std::string> variables{};
1398 parseGPIO(element, variables);
1399 ADD_FAILURE() << "Should not have reached this line.";
1400 }
1401 catch (const std::invalid_argument& e)
1402 {
1403 EXPECT_STREQ(e.what(), "Element is not a boolean");
1404 }
1405
1406 // Test where fails: Invalid property specified
1407 try
1408 {
1409 const json element = R"(
1410 {
1411 "line": 131,
1412 "foo": "bar"
1413 }
1414 )"_json;
1415 std::map<std::string, std::string> variables{};
1416 parseGPIO(element, variables);
1417 ADD_FAILURE() << "Should not have reached this line.";
1418 }
1419 catch (const std::invalid_argument& e)
1420 {
1421 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1422 }
1423
1424 // Test where fails: Invalid variable value specified
1425 try
1426 {
1427 const json element = R"(
1428 {
1429 "line": "${line}",
1430 "active_low": "${active_low}"
1431 }
1432 )"_json;
1433 std::map<std::string, std::string> variables{{"line", "-1"},
1434 {"active_low", "false"}};
1435 parseGPIO(element, variables);
1436 ADD_FAILURE() << "Should not have reached this line.";
1437 }
1438 catch (const std::invalid_argument& e)
1439 {
1440 EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
1441 }
1442 }
1443
TEST(ConfigFileParserTests,ParseI2CInterface)1444 TEST(ConfigFileParserTests, ParseI2CInterface)
1445 {
1446 // Test where works: No variables
1447 {
1448 const json element = R"(
1449 {
1450 "bus": 2,
1451 "address": "0x70"
1452 }
1453 )"_json;
1454 std::map<std::string, std::string> variables{};
1455 auto [bus, address] = parseI2CInterface(element, variables);
1456 EXPECT_EQ(bus, 2);
1457 EXPECT_EQ(address, 0x70);
1458 }
1459
1460 // Test where works: Variables specified
1461 {
1462 const json element = R"(
1463 {
1464 "bus": "${bus}",
1465 "address": "${address}"
1466 }
1467 )"_json;
1468 std::map<std::string, std::string> variables{{"bus", "3"},
1469 {"address", "0x23"}};
1470 auto [bus, address] = parseI2CInterface(element, variables);
1471 EXPECT_EQ(bus, 3);
1472 EXPECT_EQ(address, 0x23);
1473 }
1474
1475 // Test where fails: Element is not an object
1476 try
1477 {
1478 const json element = R"( [ 1, "0x70" ] )"_json;
1479 std::map<std::string, std::string> variables{};
1480 parseI2CInterface(element, variables);
1481 ADD_FAILURE() << "Should not have reached this line.";
1482 }
1483 catch (const std::invalid_argument& e)
1484 {
1485 EXPECT_STREQ(e.what(), "Element is not an object");
1486 }
1487
1488 // Test where fails: Required bus property not specified
1489 try
1490 {
1491 const json element = R"(
1492 {
1493 "address": "0x70"
1494 }
1495 )"_json;
1496 std::map<std::string, std::string> variables{};
1497 parseI2CInterface(element, variables);
1498 ADD_FAILURE() << "Should not have reached this line.";
1499 }
1500 catch (const std::invalid_argument& e)
1501 {
1502 EXPECT_STREQ(e.what(), "Required property missing: bus");
1503 }
1504
1505 // Test where fails: Required address property not specified
1506 try
1507 {
1508 const json element = R"(
1509 {
1510 "bus": 2
1511 }
1512 )"_json;
1513 std::map<std::string, std::string> variables{};
1514 parseI2CInterface(element, variables);
1515 ADD_FAILURE() << "Should not have reached this line.";
1516 }
1517 catch (const std::invalid_argument& e)
1518 {
1519 EXPECT_STREQ(e.what(), "Required property missing: address");
1520 }
1521
1522 // Test where fails: bus value is invalid
1523 try
1524 {
1525 const json element = R"(
1526 {
1527 "bus": 1.1,
1528 "address": "0x70"
1529 }
1530 )"_json;
1531 std::map<std::string, std::string> variables{};
1532 parseI2CInterface(element, variables);
1533 ADD_FAILURE() << "Should not have reached this line.";
1534 }
1535 catch (const std::invalid_argument& e)
1536 {
1537 EXPECT_STREQ(e.what(), "Element is not an integer");
1538 }
1539
1540 // Test where fails: address value is invalid
1541 try
1542 {
1543 const json element = R"(
1544 {
1545 "bus": 2,
1546 "address": 70
1547 }
1548 )"_json;
1549 std::map<std::string, std::string> variables{};
1550 parseI2CInterface(element, variables);
1551 ADD_FAILURE() << "Should not have reached this line.";
1552 }
1553 catch (const std::invalid_argument& e)
1554 {
1555 EXPECT_STREQ(e.what(), "Element is not a string");
1556 }
1557
1558 // Test where fails: Invalid property specified
1559 try
1560 {
1561 const json element = R"(
1562 {
1563 "bus": 2,
1564 "address": "0x70",
1565 "foo": "bar"
1566 }
1567 )"_json;
1568 std::map<std::string, std::string> variables{};
1569 parseI2CInterface(element, variables);
1570 ADD_FAILURE() << "Should not have reached this line.";
1571 }
1572 catch (const std::invalid_argument& e)
1573 {
1574 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1575 }
1576
1577 // Test where fails: Invalid variable value specified
1578 try
1579 {
1580 const json element = R"(
1581 {
1582 "bus": "${bus}",
1583 "address": "${address}"
1584 }
1585 )"_json;
1586 std::map<std::string, std::string> variables{{"bus", "foo"},
1587 {"address", "0x23"}};
1588 parseI2CInterface(element, variables);
1589 ADD_FAILURE() << "Should not have reached this line.";
1590 }
1591 catch (const std::invalid_argument& e)
1592 {
1593 EXPECT_STREQ(e.what(), "Element is not an integer");
1594 }
1595 }
1596
TEST(ConfigFileParserTests,ParsePowerSequencer)1597 TEST(ConfigFileParserTests, ParsePowerSequencer)
1598 {
1599 // Test where works: Has comments property: Type is "UCD90160"
1600 {
1601 const json element = R"(
1602 {
1603 "comments": [ "Power sequencer in chassis 1",
1604 "Controls VDD rails" ],
1605 "type": "UCD90160",
1606 "i2c_interface": { "bus": 3, "address": "0x11" },
1607 "power_control_gpio_name": "power-chassis-control",
1608 "power_good_gpio_name": "power-chassis-good",
1609 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ]
1610 }
1611 )"_json;
1612 std::map<std::string, std::string> variables{};
1613 MockServices services{};
1614 auto powerSequencer = parsePowerSequencer(element, variables, services);
1615 EXPECT_EQ(powerSequencer->getName(), "UCD90160");
1616 EXPECT_EQ(powerSequencer->getBus(), 3);
1617 EXPECT_EQ(powerSequencer->getAddress(), 0x11);
1618 EXPECT_EQ(powerSequencer->getPowerControlGPIOName(),
1619 "power-chassis-control");
1620 EXPECT_EQ(powerSequencer->getPowerGoodGPIOName(), "power-chassis-good");
1621 EXPECT_EQ(powerSequencer->getRails().size(), 2);
1622 EXPECT_EQ(powerSequencer->getRails()[0]->getName(), "VDD_CPU0");
1623 EXPECT_EQ(powerSequencer->getRails()[1]->getName(), "VCS_CPU1");
1624 }
1625
1626 // Test where works: No comments property: Variables specified: Type is
1627 // "UCD90320"
1628 {
1629 const json element = R"(
1630 {
1631 "type": "${type}",
1632 "i2c_interface": { "bus": "${bus}", "address": "${address}" },
1633 "power_control_gpio_name": "${power_control_gpio_name}",
1634 "power_good_gpio_name": "${power_good_gpio_name}",
1635 "rails": [ { "name": "${rail1}" }, { "name": "${rail2}" } ]
1636 }
1637 )"_json;
1638 std::map<std::string, std::string> variables{
1639 {"type", "UCD90320"},
1640 {"bus", "4"},
1641 {"address", "0x24"},
1642 {"power_control_gpio_name", "power_on"},
1643 {"power_good_gpio_name", "pgood"},
1644 {"rail1", "cpu1"},
1645 {"rail2", "cpu2"}};
1646 MockServices services{};
1647 auto powerSequencer = parsePowerSequencer(element, variables, services);
1648 EXPECT_EQ(powerSequencer->getName(), "UCD90320");
1649 EXPECT_EQ(powerSequencer->getBus(), 4);
1650 EXPECT_EQ(powerSequencer->getAddress(), 0x24);
1651 EXPECT_EQ(powerSequencer->getPowerControlGPIOName(), "power_on");
1652 EXPECT_EQ(powerSequencer->getPowerGoodGPIOName(), "pgood");
1653 EXPECT_EQ(powerSequencer->getRails().size(), 2);
1654 EXPECT_EQ(powerSequencer->getRails()[0]->getName(), "cpu1");
1655 EXPECT_EQ(powerSequencer->getRails()[1]->getName(), "cpu2");
1656 }
1657
1658 // Test where works: Type is "gpios_only_device"
1659 {
1660 const json element = R"(
1661 {
1662 "type": "gpios_only_device",
1663 "power_control_gpio_name": "power-chassis-control",
1664 "power_good_gpio_name": "power-chassis-good"
1665 }
1666 )"_json;
1667 std::map<std::string, std::string> variables{};
1668 MockServices services{};
1669 auto powerSequencer = parsePowerSequencer(element, variables, services);
1670 EXPECT_EQ(powerSequencer->getName(), "gpios_only_device");
1671 EXPECT_EQ(powerSequencer->getBus(), 0);
1672 EXPECT_EQ(powerSequencer->getAddress(), 0);
1673 EXPECT_EQ(powerSequencer->getPowerControlGPIOName(),
1674 "power-chassis-control");
1675 EXPECT_EQ(powerSequencer->getPowerGoodGPIOName(), "power-chassis-good");
1676 EXPECT_EQ(powerSequencer->getRails().size(), 0);
1677 }
1678
1679 // Test where fails: Element is not an object
1680 try
1681 {
1682 const json element = R"( [ "vdda", "vddb" ] )"_json;
1683 std::map<std::string, std::string> variables{};
1684 MockServices services{};
1685 parsePowerSequencer(element, variables, services);
1686 ADD_FAILURE() << "Should not have reached this line.";
1687 }
1688 catch (const std::invalid_argument& e)
1689 {
1690 EXPECT_STREQ(e.what(), "Element is not an object");
1691 }
1692
1693 // Test where fails: Required type property not specified
1694 try
1695 {
1696 const json element = R"(
1697 {
1698 "i2c_interface": { "bus": 3, "address": "0x11" },
1699 "power_control_gpio_name": "power-chassis-control",
1700 "power_good_gpio_name": "power-chassis-good",
1701 "rails": []
1702 }
1703 )"_json;
1704 std::map<std::string, std::string> variables{};
1705 MockServices services{};
1706 parsePowerSequencer(element, variables, services);
1707 ADD_FAILURE() << "Should not have reached this line.";
1708 }
1709 catch (const std::invalid_argument& e)
1710 {
1711 EXPECT_STREQ(e.what(), "Required property missing: type");
1712 }
1713
1714 // Test where fails: Required i2c_interface property not specified
1715 try
1716 {
1717 const json element = R"(
1718 {
1719 "type": "UCD90320",
1720 "power_control_gpio_name": "power-chassis-control",
1721 "power_good_gpio_name": "power-chassis-good",
1722 "rails": []
1723 }
1724 )"_json;
1725 std::map<std::string, std::string> variables{};
1726 MockServices services{};
1727 parsePowerSequencer(element, variables, services);
1728 ADD_FAILURE() << "Should not have reached this line.";
1729 }
1730 catch (const std::invalid_argument& e)
1731 {
1732 EXPECT_STREQ(e.what(), "Required property missing: i2c_interface");
1733 }
1734
1735 // Test where fails: Required power_control_gpio_name property not specified
1736 try
1737 {
1738 const json element = R"(
1739 {
1740 "type": "UCD90320",
1741 "i2c_interface": { "bus": 3, "address": "0x11" },
1742 "power_good_gpio_name": "power-chassis-good",
1743 "rails": []
1744 }
1745 )"_json;
1746 std::map<std::string, std::string> variables{};
1747 MockServices services{};
1748 parsePowerSequencer(element, variables, services);
1749 ADD_FAILURE() << "Should not have reached this line.";
1750 }
1751 catch (const std::invalid_argument& e)
1752 {
1753 EXPECT_STREQ(e.what(),
1754 "Required property missing: power_control_gpio_name");
1755 }
1756
1757 // Test where fails: Required power_good_gpio_name property not specified
1758 try
1759 {
1760 const json element = R"(
1761 {
1762 "type": "UCD90320",
1763 "i2c_interface": { "bus": 3, "address": "0x11" },
1764 "power_control_gpio_name": "power-chassis-control",
1765 "rails": []
1766 }
1767 )"_json;
1768 std::map<std::string, std::string> variables{};
1769 MockServices services{};
1770 parsePowerSequencer(element, variables, services);
1771 ADD_FAILURE() << "Should not have reached this line.";
1772 }
1773 catch (const std::invalid_argument& e)
1774 {
1775 EXPECT_STREQ(e.what(),
1776 "Required property missing: power_good_gpio_name");
1777 }
1778
1779 // Test where fails: Required rails property not specified
1780 try
1781 {
1782 const json element = R"(
1783 {
1784 "type": "UCD90320",
1785 "i2c_interface": { "bus": 3, "address": "0x11" },
1786 "power_control_gpio_name": "power-chassis-control",
1787 "power_good_gpio_name": "power-chassis-good"
1788 }
1789 )"_json;
1790 std::map<std::string, std::string> variables{};
1791 MockServices services{};
1792 parsePowerSequencer(element, variables, services);
1793 ADD_FAILURE() << "Should not have reached this line.";
1794 }
1795 catch (const std::invalid_argument& e)
1796 {
1797 EXPECT_STREQ(e.what(), "Required property missing: rails");
1798 }
1799
1800 // Test where fails: type value is invalid: Not a string
1801 try
1802 {
1803 const json element = R"(
1804 {
1805 "type": true,
1806 "i2c_interface": { "bus": 3, "address": "0x11" },
1807 "power_control_gpio_name": "power-chassis-control",
1808 "power_good_gpio_name": "power-chassis-good",
1809 "rails": []
1810 }
1811 )"_json;
1812 std::map<std::string, std::string> variables{};
1813 MockServices services{};
1814 parsePowerSequencer(element, variables, services);
1815 ADD_FAILURE() << "Should not have reached this line.";
1816 }
1817 catch (const std::invalid_argument& e)
1818 {
1819 EXPECT_STREQ(e.what(), "Element is not a string");
1820 }
1821
1822 // Test where fails: type value is invalid: Not a supported type
1823 try
1824 {
1825 const json element = R"(
1826 {
1827 "type": "foo_bar",
1828 "i2c_interface": { "bus": 3, "address": "0x11" },
1829 "power_control_gpio_name": "power-chassis-control",
1830 "power_good_gpio_name": "power-chassis-good",
1831 "rails": []
1832 }
1833 )"_json;
1834 std::map<std::string, std::string> variables{};
1835 MockServices services{};
1836 parsePowerSequencer(element, variables, services);
1837 ADD_FAILURE() << "Should not have reached this line.";
1838 }
1839 catch (const std::invalid_argument& e)
1840 {
1841 EXPECT_STREQ(e.what(), "Invalid power sequencer type: foo_bar");
1842 }
1843
1844 // Test where fails: i2c_interface value is invalid
1845 try
1846 {
1847 const json element = R"(
1848 {
1849 "type": "UCD90320",
1850 "i2c_interface": 3,
1851 "power_control_gpio_name": "power-chassis-control",
1852 "power_good_gpio_name": "power-chassis-good",
1853 "rails": []
1854 }
1855 )"_json;
1856 std::map<std::string, std::string> variables{};
1857 MockServices services{};
1858 parsePowerSequencer(element, variables, services);
1859 ADD_FAILURE() << "Should not have reached this line.";
1860 }
1861 catch (const std::invalid_argument& e)
1862 {
1863 EXPECT_STREQ(e.what(), "Element is not an object");
1864 }
1865
1866 // Test where fails: power_control_gpio_name value is invalid
1867 try
1868 {
1869 const json element = R"(
1870 {
1871 "type": "UCD90320",
1872 "i2c_interface": { "bus": 3, "address": "0x11" },
1873 "power_control_gpio_name": [],
1874 "power_good_gpio_name": "power-chassis-good",
1875 "rails": []
1876 }
1877 )"_json;
1878 std::map<std::string, std::string> variables{};
1879 MockServices services{};
1880 parsePowerSequencer(element, variables, services);
1881 ADD_FAILURE() << "Should not have reached this line.";
1882 }
1883 catch (const std::invalid_argument& e)
1884 {
1885 EXPECT_STREQ(e.what(), "Element is not a string");
1886 }
1887
1888 // Test where fails: power_good_gpio_name value is invalid
1889 try
1890 {
1891 const json element = R"(
1892 {
1893 "type": "UCD90320",
1894 "i2c_interface": { "bus": 3, "address": "0x11" },
1895 "power_control_gpio_name": "power-chassis-control",
1896 "power_good_gpio_name": 12,
1897 "rails": []
1898 }
1899 )"_json;
1900 std::map<std::string, std::string> variables{};
1901 MockServices services{};
1902 parsePowerSequencer(element, variables, services);
1903 ADD_FAILURE() << "Should not have reached this line.";
1904 }
1905 catch (const std::invalid_argument& e)
1906 {
1907 EXPECT_STREQ(e.what(), "Element is not a string");
1908 }
1909
1910 // Test where fails: rails value is invalid
1911 try
1912 {
1913 const json element = R"(
1914 {
1915 "type": "UCD90320",
1916 "i2c_interface": { "bus": 3, "address": "0x11" },
1917 "power_control_gpio_name": "power-chassis-control",
1918 "power_good_gpio_name": "power-chassis-good",
1919 "rails": [ { "name": 33 } ]
1920 }
1921 )"_json;
1922 std::map<std::string, std::string> variables{};
1923 MockServices services{};
1924 parsePowerSequencer(element, variables, services);
1925 ADD_FAILURE() << "Should not have reached this line.";
1926 }
1927 catch (const std::invalid_argument& e)
1928 {
1929 EXPECT_STREQ(e.what(), "Element is not a string");
1930 }
1931
1932 // Test where fails: Invalid property specified
1933 try
1934 {
1935 const json element = R"(
1936 {
1937 "type": "UCD90320",
1938 "i2c_interface": { "bus": 3, "address": "0x11" },
1939 "power_control_gpio_name": "power-chassis-control",
1940 "power_good_gpio_name": "power-chassis-good",
1941 "driver_name": "foo",
1942 "rails": []
1943 }
1944 )"_json;
1945 std::map<std::string, std::string> variables{};
1946 MockServices services{};
1947 parsePowerSequencer(element, variables, services);
1948 ADD_FAILURE() << "Should not have reached this line.";
1949 }
1950 catch (const std::invalid_argument& e)
1951 {
1952 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1953 }
1954
1955 // Test where fails: Invalid variable value specified
1956 try
1957 {
1958 const json element = R"(
1959 {
1960 "type": "UCD90320",
1961 "i2c_interface": { "bus": "${bus}", "address": "0x11" },
1962 "power_control_gpio_name": "power-chassis-control",
1963 "power_good_gpio_name": "power-chassis-good",
1964 "rails": []
1965 }
1966 )"_json;
1967 std::map<std::string, std::string> variables{{"bus", "two"}};
1968 MockServices services{};
1969 parsePowerSequencer(element, variables, services);
1970 ADD_FAILURE() << "Should not have reached this line.";
1971 }
1972 catch (const std::invalid_argument& e)
1973 {
1974 EXPECT_STREQ(e.what(), "Element is not an integer");
1975 }
1976 }
1977
TEST(ConfigFileParserTests,ParsePowerSequencerArray)1978 TEST(ConfigFileParserTests, ParsePowerSequencerArray)
1979 {
1980 // Test where works: Array is empty
1981 {
1982 const json element = R"(
1983 [
1984 ]
1985 )"_json;
1986 std::map<std::string, std::string> variables{};
1987 MockServices services{};
1988 auto powerSequencers =
1989 parsePowerSequencerArray(element, variables, services);
1990 EXPECT_EQ(powerSequencers.size(), 0);
1991 }
1992
1993 // Test where works: Array is not empty
1994 {
1995 const json element = R"(
1996 [
1997 {
1998 "type": "UCD90160",
1999 "i2c_interface": { "bus": 3, "address": "0x11" },
2000 "power_control_gpio_name": "power-chassis-control1",
2001 "power_good_gpio_name": "power-chassis-good1",
2002 "rails": []
2003 },
2004 {
2005 "type": "UCD90320",
2006 "i2c_interface": { "bus": 4, "address": "0x70" },
2007 "power_control_gpio_name": "power-chassis-control2",
2008 "power_good_gpio_name": "power-chassis-good2",
2009 "rails": []
2010 }
2011 ]
2012 )"_json;
2013 std::map<std::string, std::string> variables{};
2014 MockServices services{};
2015 auto powerSequencers =
2016 parsePowerSequencerArray(element, variables, services);
2017 EXPECT_EQ(powerSequencers.size(), 2);
2018 EXPECT_EQ(powerSequencers[0]->getName(), "UCD90160");
2019 EXPECT_EQ(powerSequencers[0]->getBus(), 3);
2020 EXPECT_EQ(powerSequencers[0]->getAddress(), 0x11);
2021 EXPECT_EQ(powerSequencers[1]->getName(), "UCD90320");
2022 EXPECT_EQ(powerSequencers[1]->getBus(), 4);
2023 EXPECT_EQ(powerSequencers[1]->getAddress(), 0x70);
2024 }
2025
2026 // Test where works: Variables specified
2027 {
2028 const json element = R"(
2029 [
2030 {
2031 "type": "UCD90160",
2032 "i2c_interface": { "bus": "${bus1}", "address": "${address1}" },
2033 "power_control_gpio_name": "power-chassis-control1",
2034 "power_good_gpio_name": "power-chassis-good1",
2035 "rails": []
2036 },
2037 {
2038 "type": "UCD90320",
2039 "i2c_interface": { "bus": "${bus2}", "address": "${address2}" },
2040 "power_control_gpio_name": "power-chassis-control2",
2041 "power_good_gpio_name": "power-chassis-good2",
2042 "rails": []
2043 }
2044 ]
2045 )"_json;
2046 std::map<std::string, std::string> variables{
2047 {"bus1", "5"},
2048 {"address1", "0x22"},
2049 {"bus2", "7"},
2050 {"address2", "0x49"}};
2051 MockServices services{};
2052 auto powerSequencers =
2053 parsePowerSequencerArray(element, variables, services);
2054 EXPECT_EQ(powerSequencers.size(), 2);
2055 EXPECT_EQ(powerSequencers[0]->getName(), "UCD90160");
2056 EXPECT_EQ(powerSequencers[0]->getBus(), 5);
2057 EXPECT_EQ(powerSequencers[0]->getAddress(), 0x22);
2058 EXPECT_EQ(powerSequencers[1]->getName(), "UCD90320");
2059 EXPECT_EQ(powerSequencers[1]->getBus(), 7);
2060 EXPECT_EQ(powerSequencers[1]->getAddress(), 0x49);
2061 }
2062
2063 // Test where fails: Element is not an array
2064 try
2065 {
2066 const json element = R"(
2067 {
2068 "foo": "bar"
2069 }
2070 )"_json;
2071 std::map<std::string, std::string> variables{};
2072 MockServices services{};
2073 parsePowerSequencerArray(element, variables, services);
2074 ADD_FAILURE() << "Should not have reached this line.";
2075 }
2076 catch (const std::invalid_argument& e)
2077 {
2078 EXPECT_STREQ(e.what(), "Element is not an array");
2079 }
2080
2081 // Test where fails: Element within array is invalid
2082 try
2083 {
2084 const json element = R"(
2085 [
2086 {
2087 "type": "UCD90160",
2088 "i2c_interface": { "bus": 3, "address": "0x11" },
2089 "power_control_gpio_name": "power-chassis-control1",
2090 "power_good_gpio_name": "power-chassis-good1",
2091 "rails": []
2092 },
2093 true
2094 ]
2095 )"_json;
2096 std::map<std::string, std::string> variables{};
2097 MockServices services{};
2098 parsePowerSequencerArray(element, variables, services);
2099 ADD_FAILURE() << "Should not have reached this line.";
2100 }
2101 catch (const std::invalid_argument& e)
2102 {
2103 EXPECT_STREQ(e.what(), "Element is not an object");
2104 }
2105
2106 // Test where fails: Invalid variable value specified
2107 try
2108 {
2109 const json element = R"(
2110 [
2111 {
2112 "type": "UCD90320",
2113 "i2c_interface": { "bus": "${bus}", "address": "${address}" },
2114 "power_control_gpio_name": "power-chassis-control",
2115 "power_good_gpio_name": "power-chassis-good",
2116 "rails": []
2117 }
2118 ]
2119 )"_json;
2120 std::map<std::string, std::string> variables{{"bus", "7"},
2121 {"address", "70"}};
2122 MockServices services{};
2123 parsePowerSequencerArray(element, variables, services);
2124 ADD_FAILURE() << "Should not have reached this line.";
2125 }
2126 catch (const std::invalid_argument& e)
2127 {
2128 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2129 }
2130 }
2131
TEST(ConfigFileParserTests,ParseRail)2132 TEST(ConfigFileParserTests, ParseRail)
2133 {
2134 // Test where works: Only required properties specified
2135 {
2136 const json element = R"(
2137 {
2138 "name": "VDD_CPU0"
2139 }
2140 )"_json;
2141 std::map<std::string, std::string> variables{};
2142 auto rail = parseRail(element, variables);
2143 EXPECT_EQ(rail->getName(), "VDD_CPU0");
2144 EXPECT_FALSE(rail->getPresence().has_value());
2145 EXPECT_FALSE(rail->getPage().has_value());
2146 EXPECT_FALSE(rail->isPowerSupplyRail());
2147 EXPECT_FALSE(rail->getCheckStatusVout());
2148 EXPECT_FALSE(rail->getCompareVoltageToLimit());
2149 EXPECT_FALSE(rail->getGPIO().has_value());
2150 }
2151
2152 // Test where works: All properties specified
2153 {
2154 const json element = R"(
2155 {
2156 "name": "12.0VB",
2157 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply1",
2158 "page": 11,
2159 "is_power_supply_rail": true,
2160 "check_status_vout": true,
2161 "compare_voltage_to_limit": true,
2162 "gpio": { "line": 60, "active_low": true }
2163 }
2164 )"_json;
2165 std::map<std::string, std::string> variables{};
2166 auto rail = parseRail(element, variables);
2167 EXPECT_EQ(rail->getName(), "12.0VB");
2168 EXPECT_TRUE(rail->getPresence().has_value());
2169 EXPECT_EQ(rail->getPresence().value(),
2170 "/xyz/openbmc_project/inventory/system/chassis/powersupply1");
2171 EXPECT_TRUE(rail->getPage().has_value());
2172 EXPECT_EQ(rail->getPage().value(), 11);
2173 EXPECT_TRUE(rail->isPowerSupplyRail());
2174 EXPECT_TRUE(rail->getCheckStatusVout());
2175 EXPECT_TRUE(rail->getCompareVoltageToLimit());
2176 EXPECT_TRUE(rail->getGPIO().has_value());
2177 EXPECT_EQ(rail->getGPIO().value().line, 60);
2178 EXPECT_TRUE(rail->getGPIO().value().activeLow);
2179 }
2180
2181 // Test where works: Variables specified
2182 {
2183 const json element = R"(
2184 {
2185 "name": "${name}",
2186 "presence": "${presence}",
2187 "page": "${page}",
2188 "is_power_supply_rail": "${is_power_supply_rail}",
2189 "check_status_vout": "${check_status_vout}",
2190 "compare_voltage_to_limit": "${compare_voltage_to_limit}",
2191 "gpio": { "line": "${line}", "active_low": true }
2192 }
2193 )"_json;
2194 std::map<std::string, std::string> variables{
2195 {"name", "vdd"},
2196 {"presence",
2197 "/xyz/openbmc_project/inventory/system/chassis/powersupply2"},
2198 {"page", "9"},
2199 {"is_power_supply_rail", "true"},
2200 {"check_status_vout", "false"},
2201 {"compare_voltage_to_limit", "true"},
2202 {"line", "72"}};
2203 auto rail = parseRail(element, variables);
2204 EXPECT_EQ(rail->getName(), "vdd");
2205 EXPECT_TRUE(rail->getPresence().has_value());
2206 EXPECT_EQ(rail->getPresence().value(),
2207 "/xyz/openbmc_project/inventory/system/chassis/powersupply2");
2208 EXPECT_TRUE(rail->getPage().has_value());
2209 EXPECT_EQ(rail->getPage().value(), 9);
2210 EXPECT_TRUE(rail->isPowerSupplyRail());
2211 EXPECT_FALSE(rail->getCheckStatusVout());
2212 EXPECT_TRUE(rail->getCompareVoltageToLimit());
2213 EXPECT_TRUE(rail->getGPIO().has_value());
2214 EXPECT_EQ(rail->getGPIO().value().line, 72);
2215 EXPECT_TRUE(rail->getGPIO().value().activeLow);
2216 }
2217
2218 // Test where fails: Element is not an object
2219 try
2220 {
2221 const json element = R"( [ "vdda", "vddb" ] )"_json;
2222 std::map<std::string, std::string> variables{};
2223 parseRail(element, variables);
2224 ADD_FAILURE() << "Should not have reached this line.";
2225 }
2226 catch (const std::invalid_argument& e)
2227 {
2228 EXPECT_STREQ(e.what(), "Element is not an object");
2229 }
2230
2231 // Test where fails: Required name property not specified
2232 try
2233 {
2234 const json element = R"(
2235 {
2236 "page": 11
2237 }
2238 )"_json;
2239 std::map<std::string, std::string> variables{};
2240 parseRail(element, variables);
2241 ADD_FAILURE() << "Should not have reached this line.";
2242 }
2243 catch (const std::invalid_argument& e)
2244 {
2245 EXPECT_STREQ(e.what(), "Required property missing: name");
2246 }
2247
2248 // Test where fails: name value is invalid
2249 try
2250 {
2251 const json element = R"(
2252 {
2253 "name": 31,
2254 "page": 11
2255 }
2256 )"_json;
2257 std::map<std::string, std::string> variables{};
2258 parseRail(element, variables);
2259 ADD_FAILURE() << "Should not have reached this line.";
2260 }
2261 catch (const std::invalid_argument& e)
2262 {
2263 EXPECT_STREQ(e.what(), "Element is not a string");
2264 }
2265
2266 // Test where fails: presence value is invalid
2267 try
2268 {
2269 const json element = R"(
2270 {
2271 "name": "VCS_CPU1",
2272 "presence": false
2273 }
2274 )"_json;
2275 std::map<std::string, std::string> variables{};
2276 parseRail(element, variables);
2277 ADD_FAILURE() << "Should not have reached this line.";
2278 }
2279 catch (const std::invalid_argument& e)
2280 {
2281 EXPECT_STREQ(e.what(), "Element is not a string");
2282 }
2283
2284 // Test where fails: page value is invalid
2285 try
2286 {
2287 const json element = R"(
2288 {
2289 "name": "VCS_CPU1",
2290 "page": 256
2291 }
2292 )"_json;
2293 std::map<std::string, std::string> variables{};
2294 parseRail(element, variables);
2295 ADD_FAILURE() << "Should not have reached this line.";
2296 }
2297 catch (const std::invalid_argument& e)
2298 {
2299 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
2300 }
2301
2302 // Test where fails: is_power_supply_rail value is invalid
2303 try
2304 {
2305 const json element = R"(
2306 {
2307 "name": "12.0VA",
2308 "is_power_supply_rail": "true"
2309 }
2310 )"_json;
2311 std::map<std::string, std::string> variables{};
2312 parseRail(element, variables);
2313 ADD_FAILURE() << "Should not have reached this line.";
2314 }
2315 catch (const std::invalid_argument& e)
2316 {
2317 EXPECT_STREQ(e.what(), "Element is not a boolean");
2318 }
2319
2320 // Test where fails: check_status_vout value is invalid
2321 try
2322 {
2323 const json element = R"(
2324 {
2325 "name": "VCS_CPU1",
2326 "check_status_vout": "false"
2327 }
2328 )"_json;
2329 std::map<std::string, std::string> variables{};
2330 parseRail(element, variables);
2331 ADD_FAILURE() << "Should not have reached this line.";
2332 }
2333 catch (const std::invalid_argument& e)
2334 {
2335 EXPECT_STREQ(e.what(), "Element is not a boolean");
2336 }
2337
2338 // Test where fails: compare_voltage_to_limit value is invalid
2339 try
2340 {
2341 const json element = R"(
2342 {
2343 "name": "VCS_CPU1",
2344 "compare_voltage_to_limit": 23
2345 }
2346 )"_json;
2347 std::map<std::string, std::string> variables{};
2348 parseRail(element, variables);
2349 ADD_FAILURE() << "Should not have reached this line.";
2350 }
2351 catch (const std::invalid_argument& e)
2352 {
2353 EXPECT_STREQ(e.what(), "Element is not a boolean");
2354 }
2355
2356 // Test where fails: gpio value is invalid
2357 try
2358 {
2359 const json element = R"(
2360 {
2361 "name": "VCS_CPU1",
2362 "gpio": 131
2363 }
2364 )"_json;
2365 std::map<std::string, std::string> variables{};
2366 parseRail(element, variables);
2367 ADD_FAILURE() << "Should not have reached this line.";
2368 }
2369 catch (const std::invalid_argument& e)
2370 {
2371 EXPECT_STREQ(e.what(), "Element is not an object");
2372 }
2373
2374 // Test where fails: check_status_vout is true and page not specified
2375 try
2376 {
2377 const json element = R"(
2378 {
2379 "name": "VCS_CPU1",
2380 "check_status_vout": true
2381 }
2382 )"_json;
2383 std::map<std::string, std::string> variables{};
2384 parseRail(element, variables);
2385 ADD_FAILURE() << "Should not have reached this line.";
2386 }
2387 catch (const std::invalid_argument& e)
2388 {
2389 EXPECT_STREQ(e.what(), "Required property missing: page");
2390 }
2391
2392 // Test where fails: compare_voltage_to_limit is true and page not
2393 // specified
2394 try
2395 {
2396 const json element = R"(
2397 {
2398 "name": "VCS_CPU1",
2399 "compare_voltage_to_limit": true
2400 }
2401 )"_json;
2402 std::map<std::string, std::string> variables{};
2403 parseRail(element, variables);
2404 ADD_FAILURE() << "Should not have reached this line.";
2405 }
2406 catch (const std::invalid_argument& e)
2407 {
2408 EXPECT_STREQ(e.what(), "Required property missing: page");
2409 }
2410
2411 // Test where fails: Invalid property specified
2412 try
2413 {
2414 const json element = R"(
2415 {
2416 "name": "VCS_CPU1",
2417 "foo": "bar"
2418 }
2419 )"_json;
2420 std::map<std::string, std::string> variables{};
2421 parseRail(element, variables);
2422 ADD_FAILURE() << "Should not have reached this line.";
2423 }
2424 catch (const std::invalid_argument& e)
2425 {
2426 EXPECT_STREQ(e.what(), "Element contains an invalid property");
2427 }
2428
2429 // Test where fails: Undefined variable specified
2430 try
2431 {
2432 const json element = R"(
2433 {
2434 "name": "12.0VB",
2435 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply${chassis}"
2436 }
2437 )"_json;
2438 std::map<std::string, std::string> variables{{"foo", "bar"}};
2439 parseRail(element, variables);
2440 ADD_FAILURE() << "Should not have reached this line.";
2441 }
2442 catch (const std::invalid_argument& e)
2443 {
2444 EXPECT_STREQ(e.what(), "Undefined variable: chassis");
2445 }
2446 }
2447
TEST(ConfigFileParserTests,ParseRailArray)2448 TEST(ConfigFileParserTests, ParseRailArray)
2449 {
2450 // Test where works: Array is empty
2451 {
2452 const json element = R"(
2453 [
2454 ]
2455 )"_json;
2456 std::map<std::string, std::string> variables{};
2457 auto rails = parseRailArray(element, variables);
2458 EXPECT_EQ(rails.size(), 0);
2459 }
2460
2461 // Test where works: Array is not empty
2462 {
2463 const json element = R"(
2464 [
2465 { "name": "VDD_CPU0" },
2466 { "name": "VCS_CPU1" }
2467 ]
2468 )"_json;
2469 std::map<std::string, std::string> variables{};
2470 auto rails = parseRailArray(element, variables);
2471 EXPECT_EQ(rails.size(), 2);
2472 EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
2473 EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
2474 }
2475
2476 // Test where works: Variables specified
2477 {
2478 const json element = R"(
2479 [
2480 { "name": "${rail1}" },
2481 { "name": "${rail2}" },
2482 { "name": "${rail3}" }
2483 ]
2484 )"_json;
2485 std::map<std::string, std::string> variables{
2486 {"rail1", "foo"}, {"rail2", "bar"}, {"rail3", "baz"}};
2487 auto rails = parseRailArray(element, variables);
2488 EXPECT_EQ(rails.size(), 3);
2489 EXPECT_EQ(rails[0]->getName(), "foo");
2490 EXPECT_EQ(rails[1]->getName(), "bar");
2491 EXPECT_EQ(rails[2]->getName(), "baz");
2492 }
2493
2494 // Test where fails: Element is not an array
2495 try
2496 {
2497 const json element = R"(
2498 {
2499 "foo": "bar"
2500 }
2501 )"_json;
2502 std::map<std::string, std::string> variables{};
2503 parseRailArray(element, variables);
2504 ADD_FAILURE() << "Should not have reached this line.";
2505 }
2506 catch (const std::invalid_argument& e)
2507 {
2508 EXPECT_STREQ(e.what(), "Element is not an array");
2509 }
2510
2511 // Test where fails: Element within array is invalid
2512 try
2513 {
2514 const json element = R"(
2515 [
2516 { "name": "VDD_CPU0" },
2517 23
2518 ]
2519 )"_json;
2520 std::map<std::string, std::string> variables{};
2521 parseRailArray(element, variables);
2522 ADD_FAILURE() << "Should not have reached this line.";
2523 }
2524 catch (const std::invalid_argument& e)
2525 {
2526 EXPECT_STREQ(e.what(), "Element is not an object");
2527 }
2528
2529 // Test where fails: Invalid variable value specified
2530 try
2531 {
2532 const json element = R"(
2533 [
2534 { "name": "VDD_CPU0", "page": "${page1}" },
2535 { "name": "VCS_CPU1", "page": "${page2}" }
2536 ]
2537 )"_json;
2538 std::map<std::string, std::string> variables{{"page1", "11"},
2539 {"page2", "-1"}};
2540 parseRailArray(element, variables);
2541 ADD_FAILURE() << "Should not have reached this line.";
2542 }
2543 catch (const std::invalid_argument& e)
2544 {
2545 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
2546 }
2547 }
2548
TEST(ConfigFileParserTests,ParseRoot)2549 TEST(ConfigFileParserTests, ParseRoot)
2550 {
2551 // Test where works: Only required properties specified
2552 {
2553 const json element = R"(
2554 {
2555 "chassis": [
2556 {
2557 "number": 1,
2558 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
2559 "power_sequencers": []
2560 },
2561 {
2562 "number": 2,
2563 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
2564 "power_sequencers": []
2565 }
2566 ]
2567 }
2568 )"_json;
2569 MockServices services{};
2570 auto chassis = parseRoot(element, services);
2571 EXPECT_EQ(chassis.size(), 2);
2572 EXPECT_EQ(chassis[0]->getNumber(), 1);
2573 EXPECT_EQ(chassis[0]->getInventoryPath(),
2574 "/xyz/openbmc_project/inventory/system/chassis1");
2575 EXPECT_EQ(chassis[1]->getNumber(), 2);
2576 EXPECT_EQ(chassis[1]->getInventoryPath(),
2577 "/xyz/openbmc_project/inventory/system/chassis2");
2578 }
2579
2580 // Test where works: All properties specified
2581 {
2582 const json element = R"(
2583 {
2584 "comments": [ "Config file for a FooBar one-chassis system" ],
2585 "chassis_templates": [
2586 {
2587 "id": "foo_chassis",
2588 "number": "${chassis_number}",
2589 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
2590 "power_sequencers": []
2591 },
2592 {
2593 "id": "bar_chassis",
2594 "number": "${chassis_number}",
2595 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}",
2596 "power_sequencers": []
2597 }
2598 ],
2599 "chassis": [
2600 {
2601 "template_id": "foo_chassis",
2602 "template_variable_values": { "chassis_number": "2" }
2603 },
2604 {
2605 "template_id": "bar_chassis",
2606 "template_variable_values": { "chassis_number": "3" }
2607 }
2608 ]
2609 }
2610 )"_json;
2611 MockServices services{};
2612 auto chassis = parseRoot(element, services);
2613 EXPECT_EQ(chassis.size(), 2);
2614 EXPECT_EQ(chassis[0]->getNumber(), 2);
2615 EXPECT_EQ(chassis[0]->getInventoryPath(),
2616 "/xyz/openbmc_project/inventory/system/chassis2");
2617 EXPECT_EQ(chassis[1]->getNumber(), 3);
2618 EXPECT_EQ(chassis[1]->getInventoryPath(),
2619 "/xyz/openbmc_project/inventory/system/bar_chassis3");
2620 }
2621
2622 // Test where fails: Element is not an object
2623 try
2624 {
2625 const json element = R"( [ "VDD_CPU0", "VCS_CPU1" ] )"_json;
2626 MockServices services{};
2627 parseRoot(element, services);
2628 ADD_FAILURE() << "Should not have reached this line.";
2629 }
2630 catch (const std::invalid_argument& e)
2631 {
2632 EXPECT_STREQ(e.what(), "Element is not an object");
2633 }
2634
2635 // Test where fails: Required chassis property not specified
2636 try
2637 {
2638 const json element = R"(
2639 {
2640 "comments": [ "Config file for a FooBar one-chassis system" ],
2641 "chassis_templates": [
2642 {
2643 "id": "foo_chassis",
2644 "number": "${chassis_number}",
2645 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
2646 "power_sequencers": []
2647 }
2648 ]
2649 }
2650 )"_json;
2651 MockServices services{};
2652 parseRoot(element, services);
2653 ADD_FAILURE() << "Should not have reached this line.";
2654 }
2655 catch (const std::invalid_argument& e)
2656 {
2657 EXPECT_STREQ(e.what(), "Required property missing: chassis");
2658 }
2659
2660 // Test where fails: chassis_templates value is invalid
2661 try
2662 {
2663 const json element = R"(
2664 {
2665 "chassis_templates": true,
2666 "chassis": [
2667 {
2668 "number": 1,
2669 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
2670 "power_sequencers": []
2671 }
2672 ]
2673 }
2674 )"_json;
2675 MockServices services{};
2676 parseRoot(element, services);
2677 ADD_FAILURE() << "Should not have reached this line.";
2678 }
2679 catch (const std::invalid_argument& e)
2680 {
2681 EXPECT_STREQ(e.what(), "Element is not an array");
2682 }
2683
2684 // Test where fails: chassis value is invalid
2685 try
2686 {
2687 const json element = R"(
2688 {
2689 "chassis": [
2690 {
2691 "number": "one",
2692 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
2693 "power_sequencers": []
2694 }
2695 ]
2696 }
2697 )"_json;
2698 MockServices services{};
2699 parseRoot(element, services);
2700 ADD_FAILURE() << "Should not have reached this line.";
2701 }
2702 catch (const std::invalid_argument& e)
2703 {
2704 EXPECT_STREQ(e.what(), "Element is not an integer");
2705 }
2706
2707 // Test where fails: Invalid property specified
2708 try
2709 {
2710 const json element = R"(
2711 {
2712 "chassis": [
2713 {
2714 "number": 1,
2715 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
2716 "power_sequencers": []
2717 }
2718 ],
2719 "foo": true
2720 }
2721 )"_json;
2722 MockServices services{};
2723 parseRoot(element, services);
2724 ADD_FAILURE() << "Should not have reached this line.";
2725 }
2726 catch (const std::invalid_argument& e)
2727 {
2728 EXPECT_STREQ(e.what(), "Element contains an invalid property");
2729 }
2730 }
2731
TEST(ConfigFileParserTests,ParseVariables)2732 TEST(ConfigFileParserTests, ParseVariables)
2733 {
2734 // Test where works: No variables specified
2735 {
2736 const json element = R"(
2737 {
2738 }
2739 )"_json;
2740 auto variables = parseVariables(element);
2741 EXPECT_EQ(variables.size(), 0);
2742 }
2743
2744 // Test where works: Variables specified
2745 {
2746 const json element = R"(
2747 {
2748 "chassis_number": "2",
2749 "bus_number": "13"
2750 }
2751 )"_json;
2752 auto variables = parseVariables(element);
2753 EXPECT_EQ(variables.size(), 2);
2754 EXPECT_EQ(variables["chassis_number"], "2");
2755 EXPECT_EQ(variables["bus_number"], "13");
2756 }
2757
2758 // Test where fails: Element is not an object
2759 try
2760 {
2761 const json element = R"(
2762 [
2763 "chassis_number", "2",
2764 "bus_number", "13"
2765 ]
2766 )"_json;
2767 parseVariables(element);
2768 ADD_FAILURE() << "Should not have reached this line.";
2769 }
2770 catch (const std::invalid_argument& e)
2771 {
2772 EXPECT_STREQ(e.what(), "Element is not an object");
2773 }
2774
2775 // Test where fails: Key is not a string
2776 try
2777 {
2778 const json element = R"(
2779 {
2780 chassis_number: "2",
2781 "bus_number": "13"
2782 }
2783 )"_json;
2784 parseVariables(element);
2785 ADD_FAILURE() << "Should not have reached this line.";
2786 }
2787 catch (const json::parse_error& e)
2788 {}
2789
2790 // Test where fails: Value is not a string
2791 try
2792 {
2793 const json element = R"(
2794 {
2795 "chassis_number": "2",
2796 "bus_number": 13
2797 }
2798 )"_json;
2799 parseVariables(element);
2800 ADD_FAILURE() << "Should not have reached this line.";
2801 }
2802 catch (const std::invalid_argument& e)
2803 {
2804 EXPECT_STREQ(e.what(), "Element is not a string");
2805 }
2806
2807 // Test where fails: Key is an empty string
2808 try
2809 {
2810 const json element = R"(
2811 {
2812 "chassis_number": "2",
2813 "": "13"
2814 }
2815 )"_json;
2816 parseVariables(element);
2817 ADD_FAILURE() << "Should not have reached this line.";
2818 }
2819 catch (const std::invalid_argument& e)
2820 {
2821 EXPECT_STREQ(e.what(), "Element contains an empty string");
2822 }
2823
2824 // Test where fails: Value is an empty string
2825 try
2826 {
2827 const json element = R"(
2828 {
2829 "chassis_number": "",
2830 "bus_number": "13"
2831 }
2832 )"_json;
2833 parseVariables(element);
2834 ADD_FAILURE() << "Should not have reached this line.";
2835 }
2836 catch (const std::invalid_argument& e)
2837 {
2838 EXPECT_STREQ(e.what(), "Element contains an empty string");
2839 }
2840 }
2841