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 "config_file_parser.hpp"
17 #include "config_file_parser_error.hpp"
18 #include "rail.hpp"
19 #include "temporary_file.hpp"
20 #include "temporary_subdirectory.hpp"
21
22 #include <sys/stat.h> // for chmod()
23
24 #include <nlohmann/json.hpp>
25
26 #include <cstdint>
27 #include <exception>
28 #include <filesystem>
29 #include <fstream>
30 #include <memory>
31 #include <optional>
32 #include <stdexcept>
33 #include <string>
34 #include <vector>
35
36 #include <gtest/gtest.h>
37
38 using namespace phosphor::power::sequencer;
39 using namespace phosphor::power::sequencer::config_file_parser;
40 using namespace phosphor::power::sequencer::config_file_parser::internal;
41 using namespace phosphor::power::util;
42 using json = nlohmann::json;
43 namespace fs = std::filesystem;
44
writeConfigFile(const fs::path & pathName,const std::string & contents)45 void writeConfigFile(const fs::path& pathName, const std::string& contents)
46 {
47 std::ofstream file{pathName};
48 file << contents;
49 }
50
writeConfigFile(const fs::path & pathName,const json & contents)51 void writeConfigFile(const fs::path& pathName, const json& contents)
52 {
53 std::ofstream file{pathName};
54 file << contents;
55 }
56
TEST(ConfigFileParserTests,Find)57 TEST(ConfigFileParserTests, Find)
58 {
59 std::vector<std::string> compatibleSystemTypes{
60 "com.acme.Hardware.Chassis.Model.MegaServer4CPU",
61 "com.acme.Hardware.Chassis.Model.MegaServer",
62 "com.acme.Hardware.Chassis.Model.Server"};
63
64 // Test where works: Fully qualified system type: First in list
65 {
66 TemporarySubDirectory configFileDir;
67 fs::path configFileDirPath = configFileDir.getPath();
68
69 fs::path configFilePath = configFileDirPath;
70 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
71 writeConfigFile(configFilePath, std::string{""});
72
73 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
74 EXPECT_EQ(pathFound, configFilePath);
75 }
76
77 // Test where works: Fully qualified system type: Second in list
78 {
79 TemporarySubDirectory configFileDir;
80 fs::path configFileDirPath = configFileDir.getPath();
81
82 fs::path configFilePath = configFileDirPath;
83 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer.json";
84 writeConfigFile(configFilePath, std::string{""});
85
86 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
87 EXPECT_EQ(pathFound, configFilePath);
88 }
89
90 // Test where works: Last node in system type: Second in list
91 {
92 TemporarySubDirectory configFileDir;
93 fs::path configFileDirPath = configFileDir.getPath();
94
95 fs::path configFilePath = configFileDirPath;
96 configFilePath /= "MegaServer.json";
97 writeConfigFile(configFilePath, std::string{""});
98
99 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
100 EXPECT_EQ(pathFound, configFilePath);
101 }
102
103 // Test where works: Last node in system type: Last in list
104 {
105 TemporarySubDirectory configFileDir;
106 fs::path configFileDirPath = configFileDir.getPath();
107
108 fs::path configFilePath = configFileDirPath;
109 configFilePath /= "Server.json";
110 writeConfigFile(configFilePath, std::string{""});
111
112 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
113 EXPECT_EQ(pathFound, configFilePath);
114 }
115
116 // Test where works: System type has no '.'
117 {
118 TemporarySubDirectory configFileDir;
119 fs::path configFileDirPath = configFileDir.getPath();
120
121 fs::path configFilePath = configFileDirPath;
122 configFilePath /= "Server.json";
123 writeConfigFile(configFilePath, std::string{""});
124
125 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
126 "MegaServer", "Server"};
127 fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
128 EXPECT_EQ(pathFound, configFilePath);
129 }
130
131 // Test where fails: System type list is empty
132 {
133 TemporarySubDirectory configFileDir;
134 fs::path configFileDirPath = configFileDir.getPath();
135
136 fs::path configFilePath = configFileDirPath;
137 configFilePath /= "Server.json";
138 writeConfigFile(configFilePath, std::string{""});
139
140 std::vector<std::string> emptySystemTypes{};
141 fs::path pathFound = find(emptySystemTypes, configFileDirPath);
142 EXPECT_TRUE(pathFound.empty());
143 }
144
145 // Test where fails: Configuration file directory is empty
146 {
147 TemporarySubDirectory configFileDir;
148 fs::path configFileDirPath = configFileDir.getPath();
149
150 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
151 EXPECT_TRUE(pathFound.empty());
152 }
153
154 // Test where fails: Configuration file directory does not exist
155 {
156 fs::path configFileDirPath{"/tmp/does_not_exist_XYZ"};
157
158 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
159 EXPECT_TRUE(pathFound.empty());
160 }
161
162 // Test where fails: Configuration file directory is not readable
163 {
164 TemporarySubDirectory configFileDir;
165 fs::path configFileDirPath = configFileDir.getPath();
166 fs::permissions(configFileDirPath, fs::perms::none);
167
168 EXPECT_THROW(find(compatibleSystemTypes, configFileDirPath),
169 std::exception);
170
171 fs::permissions(configFileDirPath, fs::perms::owner_all);
172 }
173
174 // Test where fails: No matching file name found
175 {
176 TemporarySubDirectory configFileDir;
177 fs::path configFileDirPath = configFileDir.getPath();
178
179 fs::path configFilePath = configFileDirPath;
180 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer";
181 writeConfigFile(configFilePath, std::string{""});
182
183 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
184 EXPECT_TRUE(pathFound.empty());
185 }
186
187 // Test where fails: Matching file name is a directory: Fully qualified
188 {
189 TemporarySubDirectory configFileDir;
190 fs::path configFileDirPath = configFileDir.getPath();
191
192 fs::path configFilePath = configFileDirPath;
193 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
194 fs::create_directory(configFilePath);
195
196 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
197 EXPECT_TRUE(pathFound.empty());
198 }
199
200 // Test where fails: Matching file name is a directory: Last node
201 {
202 TemporarySubDirectory configFileDir;
203 fs::path configFileDirPath = configFileDir.getPath();
204
205 fs::path configFilePath = configFileDirPath;
206 configFilePath /= "MegaServer.json";
207 fs::create_directory(configFilePath);
208
209 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
210 EXPECT_TRUE(pathFound.empty());
211 }
212
213 // Test where fails: System type has no '.'
214 {
215 TemporarySubDirectory configFileDir;
216 fs::path configFileDirPath = configFileDir.getPath();
217
218 fs::path configFilePath = configFileDirPath;
219 configFilePath /= "MegaServer2CPU.json";
220 writeConfigFile(configFilePath, std::string{""});
221
222 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
223 "MegaServer", "Server", ""};
224 fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
225 EXPECT_TRUE(pathFound.empty());
226 }
227
228 // Test where fails: System type ends with '.'
229 {
230 TemporarySubDirectory configFileDir;
231 fs::path configFileDirPath = configFileDir.getPath();
232
233 fs::path configFilePath = configFileDirPath;
234 configFilePath /= "MegaServer4CPU.json";
235 writeConfigFile(configFilePath, std::string{""});
236
237 std::vector<std::string> dotAtEndSystemTypes{
238 "com.acme.Hardware.Chassis.Model.MegaServer4CPU.", "a.", "."};
239 fs::path pathFound = find(dotAtEndSystemTypes, configFileDirPath);
240 EXPECT_TRUE(pathFound.empty());
241 }
242 }
243
TEST(ConfigFileParserTests,Parse)244 TEST(ConfigFileParserTests, Parse)
245 {
246 // Test where works
247 {
248 const json configFileContents = R"(
249 {
250 "rails": [
251 {
252 "name": "VDD_CPU0",
253 "page": 11,
254 "check_status_vout": true
255 },
256 {
257 "name": "VCS_CPU1",
258 "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
259 "gpio": { "line": 60 }
260 }
261 ]
262 }
263 )"_json;
264
265 TemporaryFile configFile;
266 fs::path pathName{configFile.getPath()};
267 writeConfigFile(pathName, configFileContents);
268
269 std::vector<std::unique_ptr<Rail>> rails = parse(pathName);
270
271 EXPECT_EQ(rails.size(), 2);
272 EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
273 EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
274 }
275
276 // Test where fails: File does not exist
277 {
278 fs::path pathName{"/tmp/non_existent_file"};
279 EXPECT_THROW(parse(pathName), ConfigFileParserError);
280 }
281
282 // Test where fails: File is not readable
283 {
284 const json configFileContents = R"(
285 {
286 "rails": [
287 {
288 "name": "VDD_CPU0"
289 }
290 ]
291 }
292 )"_json;
293
294 TemporaryFile configFile;
295 fs::path pathName{configFile.getPath()};
296 writeConfigFile(pathName, configFileContents);
297
298 chmod(pathName.c_str(), 0222);
299 EXPECT_THROW(parse(pathName), ConfigFileParserError);
300 }
301
302 // Test where fails: File is not valid JSON
303 {
304 const std::string configFileContents = "] foo [";
305
306 TemporaryFile configFile;
307 fs::path pathName{configFile.getPath()};
308 writeConfigFile(pathName, configFileContents);
309
310 EXPECT_THROW(parse(pathName), ConfigFileParserError);
311 }
312
313 // Test where fails: JSON does not conform to config file format
314 {
315 const json configFileContents = R"( [ "foo", "bar" ] )"_json;
316
317 TemporaryFile configFile;
318 fs::path pathName{configFile.getPath()};
319 writeConfigFile(pathName, configFileContents);
320
321 EXPECT_THROW(parse(pathName), ConfigFileParserError);
322 }
323 }
324
TEST(ConfigFileParserTests,GetRequiredProperty)325 TEST(ConfigFileParserTests, GetRequiredProperty)
326 {
327 // Test where property exists
328 {
329 const json element = R"( { "name": "VDD_CPU0" } )"_json;
330 const json& propertyElement = getRequiredProperty(element, "name");
331 EXPECT_EQ(propertyElement.get<std::string>(), "VDD_CPU0");
332 }
333
334 // Test where property does not exist
335 try
336 {
337 const json element = R"( { "foo": 23 } )"_json;
338 getRequiredProperty(element, "name");
339 ADD_FAILURE() << "Should not have reached this line.";
340 }
341 catch (const std::invalid_argument& e)
342 {
343 EXPECT_STREQ(e.what(), "Required property missing: name");
344 }
345 }
346
TEST(ConfigFileParserTests,ParseBoolean)347 TEST(ConfigFileParserTests, ParseBoolean)
348 {
349 // Test where works: true
350 {
351 const json element = R"( true )"_json;
352 bool value = parseBoolean(element);
353 EXPECT_EQ(value, true);
354 }
355
356 // Test where works: false
357 {
358 const json element = R"( false )"_json;
359 bool value = parseBoolean(element);
360 EXPECT_EQ(value, false);
361 }
362
363 // Test where fails: Element is not a boolean
364 try
365 {
366 const json element = R"( 1 )"_json;
367 parseBoolean(element);
368 ADD_FAILURE() << "Should not have reached this line.";
369 }
370 catch (const std::invalid_argument& e)
371 {
372 EXPECT_STREQ(e.what(), "Element is not a boolean");
373 }
374 }
375
TEST(ConfigFileParserTests,ParseGPIO)376 TEST(ConfigFileParserTests, ParseGPIO)
377 {
378 // Test where works: Only required properties specified
379 {
380 const json element = R"(
381 {
382 "line": 60
383 }
384 )"_json;
385 GPIO gpio = parseGPIO(element);
386 EXPECT_EQ(gpio.line, 60);
387 EXPECT_FALSE(gpio.activeLow);
388 }
389
390 // Test where works: All properties specified
391 {
392 const json element = R"(
393 {
394 "line": 131,
395 "active_low": true
396 }
397 )"_json;
398 GPIO gpio = parseGPIO(element);
399 EXPECT_EQ(gpio.line, 131);
400 EXPECT_TRUE(gpio.activeLow);
401 }
402
403 // Test where fails: Element is not an object
404 try
405 {
406 const json element = R"( [ "vdda", "vddb" ] )"_json;
407 parseGPIO(element);
408 ADD_FAILURE() << "Should not have reached this line.";
409 }
410 catch (const std::invalid_argument& e)
411 {
412 EXPECT_STREQ(e.what(), "Element is not an object");
413 }
414
415 // Test where fails: Required line property not specified
416 try
417 {
418 const json element = R"(
419 {
420 "active_low": true
421 }
422 )"_json;
423 parseGPIO(element);
424 ADD_FAILURE() << "Should not have reached this line.";
425 }
426 catch (const std::invalid_argument& e)
427 {
428 EXPECT_STREQ(e.what(), "Required property missing: line");
429 }
430
431 // Test where fails: line value is invalid
432 try
433 {
434 const json element = R"(
435 {
436 "line": -131,
437 "active_low": true
438 }
439 )"_json;
440 parseGPIO(element);
441 ADD_FAILURE() << "Should not have reached this line.";
442 }
443 catch (const std::invalid_argument& e)
444 {
445 EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
446 }
447
448 // Test where fails: active_low value is invalid
449 try
450 {
451 const json element = R"(
452 {
453 "line": 131,
454 "active_low": "true"
455 }
456 )"_json;
457 parseGPIO(element);
458 ADD_FAILURE() << "Should not have reached this line.";
459 }
460 catch (const std::invalid_argument& e)
461 {
462 EXPECT_STREQ(e.what(), "Element is not a boolean");
463 }
464
465 // Test where fails: Invalid property specified
466 try
467 {
468 const json element = R"(
469 {
470 "line": 131,
471 "foo": "bar"
472 }
473 )"_json;
474 parseGPIO(element);
475 ADD_FAILURE() << "Should not have reached this line.";
476 }
477 catch (const std::invalid_argument& e)
478 {
479 EXPECT_STREQ(e.what(), "Element contains an invalid property");
480 }
481 }
482
TEST(ConfigFileParserTests,ParseRail)483 TEST(ConfigFileParserTests, ParseRail)
484 {
485 // Test where works: Only required properties specified
486 {
487 const json element = R"(
488 {
489 "name": "VDD_CPU0"
490 }
491 )"_json;
492 std::unique_ptr<Rail> rail = parseRail(element);
493 EXPECT_EQ(rail->getName(), "VDD_CPU0");
494 EXPECT_FALSE(rail->getPresence().has_value());
495 EXPECT_FALSE(rail->getPage().has_value());
496 EXPECT_FALSE(rail->isPowerSupplyRail());
497 EXPECT_FALSE(rail->getCheckStatusVout());
498 EXPECT_FALSE(rail->getCompareVoltageToLimit());
499 EXPECT_FALSE(rail->getGPIO().has_value());
500 }
501
502 // Test where works: All properties specified
503 {
504 const json element = R"(
505 {
506 "name": "12.0VB",
507 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply1",
508 "page": 11,
509 "is_power_supply_rail": true,
510 "check_status_vout": true,
511 "compare_voltage_to_limit": true,
512 "gpio": { "line": 60, "active_low": true }
513 }
514 )"_json;
515 std::unique_ptr<Rail> rail = parseRail(element);
516 EXPECT_EQ(rail->getName(), "12.0VB");
517 EXPECT_TRUE(rail->getPresence().has_value());
518 EXPECT_EQ(rail->getPresence().value(),
519 "/xyz/openbmc_project/inventory/system/chassis/powersupply1");
520 EXPECT_TRUE(rail->getPage().has_value());
521 EXPECT_EQ(rail->getPage().value(), 11);
522 EXPECT_TRUE(rail->isPowerSupplyRail());
523 EXPECT_TRUE(rail->getCheckStatusVout());
524 EXPECT_TRUE(rail->getCompareVoltageToLimit());
525 EXPECT_TRUE(rail->getGPIO().has_value());
526 EXPECT_EQ(rail->getGPIO().value().line, 60);
527 EXPECT_TRUE(rail->getGPIO().value().activeLow);
528 }
529
530 // Test where fails: Element is not an object
531 try
532 {
533 const json element = R"( [ "vdda", "vddb" ] )"_json;
534 parseRail(element);
535 ADD_FAILURE() << "Should not have reached this line.";
536 }
537 catch (const std::invalid_argument& e)
538 {
539 EXPECT_STREQ(e.what(), "Element is not an object");
540 }
541
542 // Test where fails: Required name property not specified
543 try
544 {
545 const json element = R"(
546 {
547 "page": 11
548 }
549 )"_json;
550 parseRail(element);
551 ADD_FAILURE() << "Should not have reached this line.";
552 }
553 catch (const std::invalid_argument& e)
554 {
555 EXPECT_STREQ(e.what(), "Required property missing: name");
556 }
557
558 // Test where fails: name value is invalid
559 try
560 {
561 const json element = R"(
562 {
563 "name": 31,
564 "page": 11
565 }
566 )"_json;
567 parseRail(element);
568 ADD_FAILURE() << "Should not have reached this line.";
569 }
570 catch (const std::invalid_argument& e)
571 {
572 EXPECT_STREQ(e.what(), "Element is not a string");
573 }
574
575 // Test where fails: presence value is invalid
576 try
577 {
578 const json element = R"(
579 {
580 "name": "VCS_CPU1",
581 "presence": false
582 }
583 )"_json;
584 parseRail(element);
585 ADD_FAILURE() << "Should not have reached this line.";
586 }
587 catch (const std::invalid_argument& e)
588 {
589 EXPECT_STREQ(e.what(), "Element is not a string");
590 }
591
592 // Test where fails: page value is invalid
593 try
594 {
595 const json element = R"(
596 {
597 "name": "VCS_CPU1",
598 "page": 256
599 }
600 )"_json;
601 parseRail(element);
602 ADD_FAILURE() << "Should not have reached this line.";
603 }
604 catch (const std::invalid_argument& e)
605 {
606 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
607 }
608
609 // Test where fails: is_power_supply_rail value is invalid
610 try
611 {
612 const json element = R"(
613 {
614 "name": "12.0VA",
615 "is_power_supply_rail": "true"
616 }
617 )"_json;
618 parseRail(element);
619 ADD_FAILURE() << "Should not have reached this line.";
620 }
621 catch (const std::invalid_argument& e)
622 {
623 EXPECT_STREQ(e.what(), "Element is not a boolean");
624 }
625
626 // Test where fails: check_status_vout value is invalid
627 try
628 {
629 const json element = R"(
630 {
631 "name": "VCS_CPU1",
632 "check_status_vout": "false"
633 }
634 )"_json;
635 parseRail(element);
636 ADD_FAILURE() << "Should not have reached this line.";
637 }
638 catch (const std::invalid_argument& e)
639 {
640 EXPECT_STREQ(e.what(), "Element is not a boolean");
641 }
642
643 // Test where fails: compare_voltage_to_limit value is invalid
644 try
645 {
646 const json element = R"(
647 {
648 "name": "VCS_CPU1",
649 "compare_voltage_to_limit": 23
650 }
651 )"_json;
652 parseRail(element);
653 ADD_FAILURE() << "Should not have reached this line.";
654 }
655 catch (const std::invalid_argument& e)
656 {
657 EXPECT_STREQ(e.what(), "Element is not a boolean");
658 }
659
660 // Test where fails: gpio value is invalid
661 try
662 {
663 const json element = R"(
664 {
665 "name": "VCS_CPU1",
666 "gpio": 131
667 }
668 )"_json;
669 parseRail(element);
670 ADD_FAILURE() << "Should not have reached this line.";
671 }
672 catch (const std::invalid_argument& e)
673 {
674 EXPECT_STREQ(e.what(), "Element is not an object");
675 }
676
677 // Test where fails: check_status_vout is true and page not specified
678 try
679 {
680 const json element = R"(
681 {
682 "name": "VCS_CPU1",
683 "check_status_vout": true
684 }
685 )"_json;
686 parseRail(element);
687 ADD_FAILURE() << "Should not have reached this line.";
688 }
689 catch (const std::invalid_argument& e)
690 {
691 EXPECT_STREQ(e.what(), "Required property missing: page");
692 }
693
694 // Test where fails: compare_voltage_to_limit is true and page not
695 // specified
696 try
697 {
698 const json element = R"(
699 {
700 "name": "VCS_CPU1",
701 "compare_voltage_to_limit": true
702 }
703 )"_json;
704 parseRail(element);
705 ADD_FAILURE() << "Should not have reached this line.";
706 }
707 catch (const std::invalid_argument& e)
708 {
709 EXPECT_STREQ(e.what(), "Required property missing: page");
710 }
711
712 // Test where fails: Invalid property specified
713 try
714 {
715 const json element = R"(
716 {
717 "name": "VCS_CPU1",
718 "foo": "bar"
719 }
720 )"_json;
721 parseRail(element);
722 ADD_FAILURE() << "Should not have reached this line.";
723 }
724 catch (const std::invalid_argument& e)
725 {
726 EXPECT_STREQ(e.what(), "Element contains an invalid property");
727 }
728 }
729
TEST(ConfigFileParserTests,ParseRailArray)730 TEST(ConfigFileParserTests, ParseRailArray)
731 {
732 // Test where works: Array is empty
733 {
734 const json element = R"(
735 [
736 ]
737 )"_json;
738 std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
739 EXPECT_EQ(rails.size(), 0);
740 }
741
742 // Test where works: Array is not empty
743 {
744 const json element = R"(
745 [
746 { "name": "VDD_CPU0" },
747 { "name": "VCS_CPU1" }
748 ]
749 )"_json;
750 std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
751 EXPECT_EQ(rails.size(), 2);
752 EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
753 EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
754 }
755
756 // Test where fails: Element is not an array
757 try
758 {
759 const json element = R"(
760 {
761 "foo": "bar"
762 }
763 )"_json;
764 parseRailArray(element);
765 ADD_FAILURE() << "Should not have reached this line.";
766 }
767 catch (const std::invalid_argument& e)
768 {
769 EXPECT_STREQ(e.what(), "Element is not an array");
770 }
771
772 // Test where fails: Element within array is invalid
773 try
774 {
775 const json element = R"(
776 [
777 { "name": "VDD_CPU0" },
778 23
779 ]
780 )"_json;
781 parseRailArray(element);
782 ADD_FAILURE() << "Should not have reached this line.";
783 }
784 catch (const std::invalid_argument& e)
785 {
786 EXPECT_STREQ(e.what(), "Element is not an object");
787 }
788 }
789
TEST(ConfigFileParserTests,ParseRoot)790 TEST(ConfigFileParserTests, ParseRoot)
791 {
792 // Test where works
793 {
794 const json element = R"(
795 {
796 "rails": [
797 {
798 "name": "VDD_CPU0",
799 "page": 11,
800 "check_status_vout": true
801 },
802 {
803 "name": "VCS_CPU1",
804 "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
805 "gpio": { "line": 60 }
806 }
807 ]
808 }
809 )"_json;
810 std::vector<std::unique_ptr<Rail>> rails = parseRoot(element);
811 EXPECT_EQ(rails.size(), 2);
812 EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
813 EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
814 }
815
816 // Test where fails: Element is not an object
817 try
818 {
819 const json element = R"( [ "VDD_CPU0", "VCS_CPU1" ] )"_json;
820 parseRoot(element);
821 ADD_FAILURE() << "Should not have reached this line.";
822 }
823 catch (const std::invalid_argument& e)
824 {
825 EXPECT_STREQ(e.what(), "Element is not an object");
826 }
827
828 // Test where fails: Required rails property not specified
829 try
830 {
831 const json element = R"(
832 {
833 }
834 )"_json;
835 parseRoot(element);
836 ADD_FAILURE() << "Should not have reached this line.";
837 }
838 catch (const std::invalid_argument& e)
839 {
840 EXPECT_STREQ(e.what(), "Required property missing: rails");
841 }
842
843 // Test where fails: rails value is invalid
844 try
845 {
846 const json element = R"(
847 {
848 "rails": 31
849 }
850 )"_json;
851 parseRoot(element);
852 ADD_FAILURE() << "Should not have reached this line.";
853 }
854 catch (const std::invalid_argument& e)
855 {
856 EXPECT_STREQ(e.what(), "Element is not an array");
857 }
858
859 // Test where fails: Invalid property specified
860 try
861 {
862 const json element = R"(
863 {
864 "rails": [
865 {
866 "name": "VDD_CPU0",
867 "page": 11,
868 "check_status_vout": true
869 }
870 ],
871 "foo": true
872 }
873 )"_json;
874 parseRoot(element);
875 ADD_FAILURE() << "Should not have reached this line.";
876 }
877 catch (const std::invalid_argument& e)
878 {
879 EXPECT_STREQ(e.what(), "Element contains an invalid property");
880 }
881 }
882
TEST(ConfigFileParserTests,ParseString)883 TEST(ConfigFileParserTests, ParseString)
884 {
885 // Test where works: Empty string
886 {
887 const json element = "";
888 std::string value = parseString(element, true);
889 EXPECT_EQ(value, "");
890 }
891
892 // Test where works: Non-empty string
893 {
894 const json element = "vdd_cpu1";
895 std::string value = parseString(element, false);
896 EXPECT_EQ(value, "vdd_cpu1");
897 }
898
899 // Test where fails: Element is not a string
900 try
901 {
902 const json element = R"( { "foo": "bar" } )"_json;
903 parseString(element);
904 ADD_FAILURE() << "Should not have reached this line.";
905 }
906 catch (const std::invalid_argument& e)
907 {
908 EXPECT_STREQ(e.what(), "Element is not a string");
909 }
910
911 // Test where fails: Empty string
912 try
913 {
914 const json element = "";
915 parseString(element);
916 ADD_FAILURE() << "Should not have reached this line.";
917 }
918 catch (const std::invalid_argument& e)
919 {
920 EXPECT_STREQ(e.what(), "Element contains an empty string");
921 }
922 }
923
TEST(ConfigFileParserTests,ParseUint8)924 TEST(ConfigFileParserTests, ParseUint8)
925 {
926 // Test where works: 0
927 {
928 const json element = R"( 0 )"_json;
929 uint8_t value = parseUint8(element);
930 EXPECT_EQ(value, 0);
931 }
932
933 // Test where works: UINT8_MAX
934 {
935 const json element = R"( 255 )"_json;
936 uint8_t value = parseUint8(element);
937 EXPECT_EQ(value, 255);
938 }
939
940 // Test where fails: Element is not an integer
941 try
942 {
943 const json element = R"( 1.03 )"_json;
944 parseUint8(element);
945 ADD_FAILURE() << "Should not have reached this line.";
946 }
947 catch (const std::invalid_argument& e)
948 {
949 EXPECT_STREQ(e.what(), "Element is not an integer");
950 }
951
952 // Test where fails: Value < 0
953 try
954 {
955 const json element = R"( -1 )"_json;
956 parseUint8(element);
957 ADD_FAILURE() << "Should not have reached this line.";
958 }
959 catch (const std::invalid_argument& e)
960 {
961 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
962 }
963
964 // Test where fails: Value > UINT8_MAX
965 try
966 {
967 const json element = R"( 256 )"_json;
968 parseUint8(element);
969 ADD_FAILURE() << "Should not have reached this line.";
970 }
971 catch (const std::invalid_argument& e)
972 {
973 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
974 }
975 }
976
TEST(ConfigFileParserTests,ParseUnsignedInteger)977 TEST(ConfigFileParserTests, ParseUnsignedInteger)
978 {
979 // Test where works: 1
980 {
981 const json element = R"( 1 )"_json;
982 unsigned int value = parseUnsignedInteger(element);
983 EXPECT_EQ(value, 1);
984 }
985
986 // Test where fails: Element is not an integer
987 try
988 {
989 const json element = R"( 1.5 )"_json;
990 parseUnsignedInteger(element);
991 ADD_FAILURE() << "Should not have reached this line.";
992 }
993 catch (const std::invalid_argument& e)
994 {
995 EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
996 }
997
998 // Test where fails: Value < 0
999 try
1000 {
1001 const json element = R"( -1 )"_json;
1002 parseUnsignedInteger(element);
1003 ADD_FAILURE() << "Should not have reached this line.";
1004 }
1005 catch (const std::invalid_argument& e)
1006 {
1007 EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
1008 }
1009 }
1010
TEST(ConfigFileParserTests,VerifyIsArray)1011 TEST(ConfigFileParserTests, VerifyIsArray)
1012 {
1013 // Test where element is an array
1014 {
1015 const json element = R"( [ "foo", "bar" ] )"_json;
1016 verifyIsArray(element);
1017 }
1018
1019 // Test where element is not an array
1020 try
1021 {
1022 const json element = R"( { "foo": "bar" } )"_json;
1023 verifyIsArray(element);
1024 ADD_FAILURE() << "Should not have reached this line.";
1025 }
1026 catch (const std::invalid_argument& e)
1027 {
1028 EXPECT_STREQ(e.what(), "Element is not an array");
1029 }
1030 }
1031
TEST(ConfigFileParserTests,VerifyIsObject)1032 TEST(ConfigFileParserTests, VerifyIsObject)
1033 {
1034 // Test where element is an object
1035 {
1036 const json element = R"( { "foo": "bar" } )"_json;
1037 verifyIsObject(element);
1038 }
1039
1040 // Test where element is not an object
1041 try
1042 {
1043 const json element = R"( [ "foo", "bar" ] )"_json;
1044 verifyIsObject(element);
1045 ADD_FAILURE() << "Should not have reached this line.";
1046 }
1047 catch (const std::invalid_argument& e)
1048 {
1049 EXPECT_STREQ(e.what(), "Element is not an object");
1050 }
1051 }
1052
TEST(ConfigFileParserTests,VerifyPropertyCount)1053 TEST(ConfigFileParserTests, VerifyPropertyCount)
1054 {
1055 // Test where element has expected number of properties
1056 {
1057 const json element = R"(
1058 {
1059 "line": 131,
1060 "active_low": true
1061 }
1062 )"_json;
1063 verifyPropertyCount(element, 2);
1064 }
1065
1066 // Test where element has unexpected number of properties
1067 try
1068 {
1069 const json element = R"(
1070 {
1071 "line": 131,
1072 "active_low": true,
1073 "foo": 1.3
1074 }
1075 )"_json;
1076 verifyPropertyCount(element, 2);
1077 ADD_FAILURE() << "Should not have reached this line.";
1078 }
1079 catch (const std::invalid_argument& e)
1080 {
1081 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1082 }
1083 }
1084