xref: /openbmc/phosphor-power/phosphor-regulators/test/config_file_parser_tests.cpp (revision 39eb08a90b751d81a3efcca6b4a0e984a6271fdc)
1 /**
2  * Copyright © 2020 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 "action.hpp"
17 #include "and_action.hpp"
18 #include "chassis.hpp"
19 #include "compare_presence_action.hpp"
20 #include "compare_vpd_action.hpp"
21 #include "config_file_parser.hpp"
22 #include "config_file_parser_error.hpp"
23 #include "configuration.hpp"
24 #include "device.hpp"
25 #include "i2c_capture_bytes_action.hpp"
26 #include "i2c_compare_bit_action.hpp"
27 #include "i2c_compare_byte_action.hpp"
28 #include "i2c_compare_bytes_action.hpp"
29 #include "i2c_interface.hpp"
30 #include "i2c_write_bit_action.hpp"
31 #include "i2c_write_byte_action.hpp"
32 #include "i2c_write_bytes_action.hpp"
33 #include "log_phase_fault_action.hpp"
34 #include "not_action.hpp"
35 #include "or_action.hpp"
36 #include "phase_fault.hpp"
37 #include "phase_fault_detection.hpp"
38 #include "pmbus_read_sensor_action.hpp"
39 #include "pmbus_utils.hpp"
40 #include "pmbus_write_vout_command_action.hpp"
41 #include "presence_detection.hpp"
42 #include "rail.hpp"
43 #include "rule.hpp"
44 #include "run_rule_action.hpp"
45 #include "sensor_monitoring.hpp"
46 #include "sensors.hpp"
47 #include "set_device_action.hpp"
48 #include "temporary_file.hpp"
49 
50 #include <sys/stat.h> // for chmod()
51 
52 #include <nlohmann/json.hpp>
53 
54 #include <cstdint>
55 #include <cstring>
56 #include <exception>
57 #include <filesystem>
58 #include <fstream>
59 #include <memory>
60 #include <optional>
61 #include <stdexcept>
62 #include <string>
63 #include <tuple>
64 #include <vector>
65 
66 #include <gtest/gtest.h>
67 
68 using namespace phosphor::power::regulators;
69 using namespace phosphor::power::regulators::config_file_parser;
70 using namespace phosphor::power::regulators::config_file_parser::internal;
71 using json = nlohmann::json;
72 
73 void writeConfigFile(const std::filesystem::path& pathName,
74                      const std::string& contents)
75 {
76     std::ofstream file{pathName};
77     file << contents;
78 }
79 
80 void writeConfigFile(const std::filesystem::path& pathName,
81                      const json& contents)
82 {
83     std::ofstream file{pathName};
84     file << contents;
85 }
86 
87 TEST(ConfigFileParserTests, Parse)
88 {
89     // Test where works
90     {
91         const json configFileContents = R"(
92             {
93               "rules": [
94                 {
95                   "id": "set_voltage_rule1",
96                   "actions": [
97                     { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
98                   ]
99                 },
100                 {
101                   "id": "set_voltage_rule2",
102                   "actions": [
103                     { "pmbus_write_vout_command": { "volts": 1.33, "format": "linear" } }
104                   ]
105                 }
106               ],
107               "chassis": [
108                 { "number": 1 },
109                 { "number": 2 },
110                 { "number": 3 }
111               ]
112             }
113         )"_json;
114 
115         TemporaryFile configFile;
116         std::filesystem::path pathName{configFile.getPath()};
117         writeConfigFile(pathName, configFileContents);
118 
119         std::vector<std::unique_ptr<Rule>> rules{};
120         std::vector<std::unique_ptr<Chassis>> chassis{};
121         std::tie(rules, chassis) = parse(pathName);
122 
123         EXPECT_EQ(rules.size(), 2);
124         EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
125         EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
126 
127         EXPECT_EQ(chassis.size(), 3);
128         EXPECT_EQ(chassis[0]->getNumber(), 1);
129         EXPECT_EQ(chassis[1]->getNumber(), 2);
130         EXPECT_EQ(chassis[2]->getNumber(), 3);
131     }
132 
133     // Test where fails: File does not exist
134     try
135     {
136         std::filesystem::path pathName{"/tmp/non_existent_file"};
137         parse(pathName);
138         ADD_FAILURE() << "Should not have reached this line.";
139     }
140     catch (const ConfigFileParserError& e)
141     {
142         // Expected exception; what() message will vary
143     }
144 
145     // Test where fails: File is not readable
146     try
147     {
148         const json configFileContents = R"(
149             {
150               "chassis": [ { "number": 1 } ]
151             }
152         )"_json;
153 
154         TemporaryFile configFile;
155         std::filesystem::path pathName{configFile.getPath()};
156         writeConfigFile(pathName, configFileContents);
157 
158         chmod(pathName.c_str(), 0222);
159 
160         parse(pathName);
161         ADD_FAILURE() << "Should not have reached this line.";
162     }
163     catch (const ConfigFileParserError& e)
164     {
165         // Expected exception; what() message will vary
166     }
167 
168     // Test where fails: File is not valid JSON
169     try
170     {
171         const std::string configFileContents = "] foo [";
172 
173         TemporaryFile configFile;
174         std::filesystem::path pathName{configFile.getPath()};
175         writeConfigFile(pathName, configFileContents);
176 
177         parse(pathName);
178         ADD_FAILURE() << "Should not have reached this line.";
179     }
180     catch (const ConfigFileParserError& e)
181     {
182         // Expected exception; what() message will vary
183     }
184 
185     // Test where fails: Error when parsing JSON elements
186     try
187     {
188         const json configFileContents = R"( { "foo": "bar" } )"_json;
189 
190         TemporaryFile configFile;
191         std::filesystem::path pathName{configFile.getPath()};
192         writeConfigFile(pathName, configFileContents);
193 
194         parse(pathName);
195         ADD_FAILURE() << "Should not have reached this line.";
196     }
197     catch (const ConfigFileParserError& e)
198     {
199         // Expected exception; what() message will vary
200     }
201 }
202 
203 TEST(ConfigFileParserTests, GetRequiredProperty)
204 {
205     // Test where property exists
206     {
207         const json element = R"( { "format": "linear" } )"_json;
208         const json& propertyElement = getRequiredProperty(element, "format");
209         EXPECT_EQ(propertyElement.get<std::string>(), "linear");
210     }
211 
212     // Test where property does not exist
213     try
214     {
215         const json element = R"( { "volts": 1.03 } )"_json;
216         getRequiredProperty(element, "format");
217         ADD_FAILURE() << "Should not have reached this line.";
218     }
219     catch (const std::invalid_argument& e)
220     {
221         EXPECT_STREQ(e.what(), "Required property missing: format");
222     }
223 }
224 
225 TEST(ConfigFileParserTests, ParseAction)
226 {
227     // Test where works: comments property specified
228     {
229         const json element = R"(
230             {
231               "comments": [ "Set output voltage." ],
232               "pmbus_write_vout_command": {
233                 "format": "linear"
234               }
235             }
236         )"_json;
237         std::unique_ptr<Action> action = parseAction(element);
238         EXPECT_NE(action.get(), nullptr);
239     }
240 
241     // Test where works: comments property not specified
242     {
243         const json element = R"(
244             {
245               "pmbus_write_vout_command": {
246                 "format": "linear"
247               }
248             }
249         )"_json;
250         std::unique_ptr<Action> action = parseAction(element);
251         EXPECT_NE(action.get(), nullptr);
252     }
253 
254     // Test where works: and action type specified
255     {
256         const json element = R"(
257             {
258               "and": [
259                 { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
260                 { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
261               ]
262             }
263         )"_json;
264         std::unique_ptr<Action> action = parseAction(element);
265         EXPECT_NE(action.get(), nullptr);
266     }
267 
268     // Test where works: compare_presence action type specified
269     {
270         const json element = R"(
271             {
272               "compare_presence":
273               {
274                 "fru": "system/chassis/motherboard/cpu3",
275                 "value": true
276               }
277             }
278         )"_json;
279         std::unique_ptr<Action> action = parseAction(element);
280         EXPECT_NE(action.get(), nullptr);
281     }
282 
283     // Test where works: compare_vpd action type specified
284     {
285         const json element = R"(
286             {
287               "compare_vpd":
288               {
289                 "fru": "system/chassis/disk_backplane",
290                 "keyword": "CCIN",
291                 "value": "2D35"
292               }
293             }
294         )"_json;
295         std::unique_ptr<Action> action = parseAction(element);
296         EXPECT_NE(action.get(), nullptr);
297     }
298 
299     // Test where works: i2c_capture_bytes action type specified
300     {
301         const json element = R"(
302             {
303               "i2c_capture_bytes": {
304                 "register": "0xA0",
305                 "count": 2
306               }
307             }
308         )"_json;
309         std::unique_ptr<Action> action = parseAction(element);
310         EXPECT_NE(action.get(), nullptr);
311     }
312 
313     // Test where works: i2c_compare_bit action type specified
314     {
315         const json element = R"(
316             {
317               "i2c_compare_bit": {
318                 "register": "0xA0",
319                 "position": 3,
320                 "value": 0
321               }
322             }
323         )"_json;
324         std::unique_ptr<Action> action = parseAction(element);
325         EXPECT_NE(action.get(), nullptr);
326     }
327 
328     // Test where works: i2c_compare_byte action type specified
329     {
330         const json element = R"(
331             {
332               "i2c_compare_byte": {
333                 "register": "0x0A",
334                 "value": "0xCC"
335               }
336             }
337         )"_json;
338         std::unique_ptr<Action> action = parseAction(element);
339         EXPECT_NE(action.get(), nullptr);
340     }
341 
342     // Test where works: i2c_compare_bytes action type specified
343     {
344         const json element = R"(
345             {
346               "i2c_compare_bytes": {
347                 "register": "0x0A",
348                 "values": [ "0xCC", "0xFF" ]
349               }
350             }
351         )"_json;
352         std::unique_ptr<Action> action = parseAction(element);
353         EXPECT_NE(action.get(), nullptr);
354     }
355 
356     // Test where works: i2c_write_bit action type specified
357     {
358         const json element = R"(
359             {
360               "i2c_write_bit": {
361                 "register": "0xA0",
362                 "position": 3,
363                 "value": 0
364               }
365             }
366         )"_json;
367         std::unique_ptr<Action> action = parseAction(element);
368         EXPECT_NE(action.get(), nullptr);
369     }
370 
371     // Test where works: i2c_write_byte action type specified
372     {
373         const json element = R"(
374             {
375               "i2c_write_byte": {
376                 "register": "0x0A",
377                 "value": "0xCC"
378               }
379             }
380         )"_json;
381         std::unique_ptr<Action> action = parseAction(element);
382         EXPECT_NE(action.get(), nullptr);
383     }
384 
385     // Test where works: i2c_write_bytes action type specified
386     {
387         const json element = R"(
388             {
389               "i2c_write_bytes": {
390                 "register": "0x0A",
391                 "values": [ "0xCC", "0xFF" ]
392               }
393             }
394         )"_json;
395         std::unique_ptr<Action> action = parseAction(element);
396         EXPECT_NE(action.get(), nullptr);
397     }
398 
399     // Test where works: if action type specified
400     {
401         const json element = R"(
402             {
403               "if":
404               {
405                 "condition": { "run_rule": "is_downlevel_regulator" },
406                 "then": [ { "run_rule": "configure_downlevel_regulator" } ],
407                 "else": [ { "run_rule": "configure_standard_regulator" } ]
408               }
409             }
410         )"_json;
411         std::unique_ptr<Action> action = parseAction(element);
412         EXPECT_NE(action.get(), nullptr);
413     }
414 
415     // Test where works: log_phase_fault action type specified
416     {
417         const json element = R"(
418             {
419               "log_phase_fault": {
420                 "type": "n+1"
421               }
422             }
423         )"_json;
424         std::unique_ptr<Action> action = parseAction(element);
425         EXPECT_NE(action.get(), nullptr);
426     }
427 
428     // Test where works: not action type specified
429     {
430         const json element = R"(
431             {
432               "not":
433               { "i2c_compare_byte": { "register": "0xA0", "value": "0xFF" } }
434             }
435         )"_json;
436         std::unique_ptr<Action> action = parseAction(element);
437         EXPECT_NE(action.get(), nullptr);
438     }
439 
440     // Test where works: or action type specified
441     {
442         const json element = R"(
443             {
444               "or": [
445                 { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
446                 { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
447               ]
448             }
449         )"_json;
450         std::unique_ptr<Action> action = parseAction(element);
451         EXPECT_NE(action.get(), nullptr);
452     }
453 
454     // Test where works: pmbus_read_sensor action type specified
455     {
456         const json element = R"(
457             {
458               "pmbus_read_sensor": {
459                 "type": "iout",
460                 "command": "0x8C",
461                 "format": "linear_11"
462               }
463             }
464         )"_json;
465         std::unique_ptr<Action> action = parseAction(element);
466         EXPECT_NE(action.get(), nullptr);
467     }
468 
469     // Test where works: pmbus_write_vout_command action type specified
470     {
471         const json element = R"(
472             {
473               "pmbus_write_vout_command": {
474                 "format": "linear"
475               }
476             }
477         )"_json;
478         std::unique_ptr<Action> action = parseAction(element);
479         EXPECT_NE(action.get(), nullptr);
480     }
481 
482     // Test where works: run_rule action type specified
483     {
484         const json element = R"(
485             {
486               "run_rule": "set_voltage_rule"
487             }
488         )"_json;
489         std::unique_ptr<Action> action = parseAction(element);
490         EXPECT_NE(action.get(), nullptr);
491     }
492 
493     // Test where works: set_device action type specified
494     {
495         const json element = R"(
496             {
497               "set_device": "io_expander2"
498             }
499         )"_json;
500         std::unique_ptr<Action> action = parseAction(element);
501         EXPECT_NE(action.get(), nullptr);
502     }
503 
504     // Test where fails: Element is not an object
505     try
506     {
507         const json element = R"( [ "0xFF", "0x01" ] )"_json;
508         parseAction(element);
509         ADD_FAILURE() << "Should not have reached this line.";
510     }
511     catch (const std::invalid_argument& e)
512     {
513         EXPECT_STREQ(e.what(), "Element is not an object");
514     }
515 
516     // Test where fails: No action type specified
517     try
518     {
519         const json element = R"(
520             {
521               "comments": [ "Set output voltage." ]
522             }
523         )"_json;
524         parseAction(element);
525         ADD_FAILURE() << "Should not have reached this line.";
526     }
527     catch (const std::invalid_argument& e)
528     {
529         EXPECT_STREQ(e.what(), "Required action type property missing");
530     }
531 
532     // Test where fails: Multiple action types specified
533     try
534     {
535         const json element = R"(
536             {
537               "pmbus_write_vout_command": { "format": "linear" },
538               "run_rule": "set_voltage_rule"
539             }
540         )"_json;
541         parseAction(element);
542         ADD_FAILURE() << "Should not have reached this line.";
543     }
544     catch (const std::invalid_argument& e)
545     {
546         EXPECT_STREQ(e.what(), "Element contains an invalid property");
547     }
548 
549     // Test where fails: Invalid property specified
550     try
551     {
552         const json element = R"(
553             {
554               "remarks": [ "Set output voltage." ],
555               "pmbus_write_vout_command": {
556                 "format": "linear"
557               }
558             }
559         )"_json;
560         parseAction(element);
561         ADD_FAILURE() << "Should not have reached this line.";
562     }
563     catch (const std::invalid_argument& e)
564     {
565         EXPECT_STREQ(e.what(), "Element contains an invalid property");
566     }
567 }
568 
569 TEST(ConfigFileParserTests, ParseActionArray)
570 {
571     // Test where works
572     {
573         const json element = R"(
574             [
575               { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
576               { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
577             ]
578         )"_json;
579         std::vector<std::unique_ptr<Action>> actions =
580             parseActionArray(element);
581         EXPECT_EQ(actions.size(), 2);
582     }
583 
584     // Test where fails: Element is not an array
585     try
586     {
587         const json element = R"(
588             {
589               "foo": "bar"
590             }
591         )"_json;
592         parseActionArray(element);
593         ADD_FAILURE() << "Should not have reached this line.";
594     }
595     catch (const std::invalid_argument& e)
596     {
597         EXPECT_STREQ(e.what(), "Element is not an array");
598     }
599 }
600 
601 TEST(ConfigFileParserTests, ParseAnd)
602 {
603     // Test where works: Element is an array with 2 actions
604     {
605         const json element = R"(
606             [
607               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
608               { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
609             ]
610         )"_json;
611         std::unique_ptr<AndAction> action = parseAnd(element);
612         EXPECT_EQ(action->getActions().size(), 2);
613     }
614 
615     // Test where fails: Element is an array with 1 action
616     try
617     {
618         const json element = R"(
619             [
620               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }
621             ]
622         )"_json;
623         parseAnd(element);
624         ADD_FAILURE() << "Should not have reached this line.";
625     }
626     catch (const std::invalid_argument& e)
627     {
628         EXPECT_STREQ(e.what(), "Array must contain two or more actions");
629     }
630 
631     // Test where fails: Element is not an array
632     try
633     {
634         const json element = R"(
635             {
636               "foo": "bar"
637             }
638         )"_json;
639         parseAnd(element);
640         ADD_FAILURE() << "Should not have reached this line.";
641     }
642     catch (const std::invalid_argument& e)
643     {
644         EXPECT_STREQ(e.what(), "Element is not an array");
645     }
646 }
647 
648 TEST(ConfigFileParserTests, ParseBitPosition)
649 {
650     // Test where works: 0
651     {
652         const json element = R"( 0 )"_json;
653         uint8_t value = parseBitPosition(element);
654         EXPECT_EQ(value, 0);
655     }
656 
657     // Test where works: 7
658     {
659         const json element = R"( 7 )"_json;
660         uint8_t value = parseBitPosition(element);
661         EXPECT_EQ(value, 7);
662     }
663 
664     // Test where fails: Element is not an integer
665     try
666     {
667         const json element = R"( 1.03 )"_json;
668         parseBitPosition(element);
669         ADD_FAILURE() << "Should not have reached this line.";
670     }
671     catch (const std::invalid_argument& e)
672     {
673         EXPECT_STREQ(e.what(), "Element is not an integer");
674     }
675 
676     // Test where fails: Value < 0
677     try
678     {
679         const json element = R"( -1 )"_json;
680         parseBitPosition(element);
681         ADD_FAILURE() << "Should not have reached this line.";
682     }
683     catch (const std::invalid_argument& e)
684     {
685         EXPECT_STREQ(e.what(), "Element is not a bit position");
686     }
687 
688     // Test where fails: Value > 7
689     try
690     {
691         const json element = R"( 8 )"_json;
692         parseBitPosition(element);
693         ADD_FAILURE() << "Should not have reached this line.";
694     }
695     catch (const std::invalid_argument& e)
696     {
697         EXPECT_STREQ(e.what(), "Element is not a bit position");
698     }
699 }
700 
701 TEST(ConfigFileParserTests, ParseBitValue)
702 {
703     // Test where works: 0
704     {
705         const json element = R"( 0 )"_json;
706         uint8_t value = parseBitValue(element);
707         EXPECT_EQ(value, 0);
708     }
709 
710     // Test where works: 1
711     {
712         const json element = R"( 1 )"_json;
713         uint8_t value = parseBitValue(element);
714         EXPECT_EQ(value, 1);
715     }
716 
717     // Test where fails: Element is not an integer
718     try
719     {
720         const json element = R"( 0.5 )"_json;
721         parseBitValue(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 is not an integer");
727     }
728 
729     // Test where fails: Value < 0
730     try
731     {
732         const json element = R"( -1 )"_json;
733         parseBitValue(element);
734         ADD_FAILURE() << "Should not have reached this line.";
735     }
736     catch (const std::invalid_argument& e)
737     {
738         EXPECT_STREQ(e.what(), "Element is not a bit value");
739     }
740 
741     // Test where fails: Value > 1
742     try
743     {
744         const json element = R"( 2 )"_json;
745         parseBitValue(element);
746         ADD_FAILURE() << "Should not have reached this line.";
747     }
748     catch (const std::invalid_argument& e)
749     {
750         EXPECT_STREQ(e.what(), "Element is not a bit value");
751     }
752 }
753 
754 TEST(ConfigFileParserTests, ParseBoolean)
755 {
756     // Test where works: true
757     {
758         const json element = R"( true )"_json;
759         bool value = parseBoolean(element);
760         EXPECT_EQ(value, true);
761     }
762 
763     // Test where works: false
764     {
765         const json element = R"( false )"_json;
766         bool value = parseBoolean(element);
767         EXPECT_EQ(value, false);
768     }
769 
770     // Test where fails: Element is not a boolean
771     try
772     {
773         const json element = R"( 1 )"_json;
774         parseBoolean(element);
775         ADD_FAILURE() << "Should not have reached this line.";
776     }
777     catch (const std::invalid_argument& e)
778     {
779         EXPECT_STREQ(e.what(), "Element is not a boolean");
780     }
781 }
782 
783 TEST(ConfigFileParserTests, ParseChassis)
784 {
785     // Test where works: Only required properties specified
786     {
787         const json element = R"(
788             {
789               "number": 1
790             }
791         )"_json;
792         std::unique_ptr<Chassis> chassis = parseChassis(element);
793         EXPECT_EQ(chassis->getNumber(), 1);
794         EXPECT_EQ(chassis->getInventoryPath(),
795                   "/xyz/openbmc_project/inventory/system/chassis");
796         EXPECT_EQ(chassis->getDevices().size(), 0);
797     }
798 
799     // Test where works: All properties specified
800     {
801         const json element = R"(
802             {
803               "comments": [ "comments property" ],
804               "number": 2,
805               "inventory_path": "system/chassis2",
806               "devices": [
807                 {
808                   "id": "vdd_regulator",
809                   "is_regulator": true,
810                   "fru": "system/chassis/motherboard/regulator2",
811                   "i2c_interface":
812                   {
813                       "bus": 1,
814                       "address": "0x70"
815                   }
816                 }
817               ]
818             }
819         )"_json;
820         std::unique_ptr<Chassis> chassis = parseChassis(element);
821         EXPECT_EQ(chassis->getNumber(), 2);
822         EXPECT_EQ(chassis->getInventoryPath(),
823                   "/xyz/openbmc_project/inventory/system/chassis2");
824         EXPECT_EQ(chassis->getDevices().size(), 1);
825         EXPECT_EQ(chassis->getDevices()[0]->getID(), "vdd_regulator");
826     }
827 
828     // Test where fails: number value is invalid
829     try
830     {
831         const json element = R"(
832             {
833               "number": 0.5
834             }
835         )"_json;
836         parseChassis(element);
837         ADD_FAILURE() << "Should not have reached this line.";
838     }
839     catch (const std::invalid_argument& e)
840     {
841         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
842     }
843 
844     // Test where fails: inventory_path is invalid: Not a string
845     try
846     {
847         const json element = R"(
848             {
849               "number": 2,
850               "inventory_path": true
851             }
852         )"_json;
853         parseChassis(element);
854         ADD_FAILURE() << "Should not have reached this line.";
855     }
856     catch (const std::invalid_argument& e)
857     {
858         EXPECT_STREQ(e.what(), "Element is not a string");
859     }
860 
861     // Test where fails: inventory_path is invalid: Empty string
862     try
863     {
864         const json element = R"(
865             {
866               "number": 2,
867               "inventory_path": ""
868             }
869         )"_json;
870         parseChassis(element);
871         ADD_FAILURE() << "Should not have reached this line.";
872     }
873     catch (const std::invalid_argument& e)
874     {
875         EXPECT_STREQ(e.what(), "Element contains an empty string");
876     }
877 
878     // Test where fails: Invalid property specified
879     try
880     {
881         const json element = R"(
882             {
883               "number": 1,
884               "foo": 2
885             }
886         )"_json;
887         parseChassis(element);
888         ADD_FAILURE() << "Should not have reached this line.";
889     }
890     catch (const std::invalid_argument& e)
891     {
892         EXPECT_STREQ(e.what(), "Element contains an invalid property");
893     }
894 
895     // Test where fails: Required number property not specified
896     try
897     {
898         const json element = R"(
899             {
900               "devices": [
901                 {
902                   "id": "vdd_regulator",
903                   "is_regulator": true,
904                   "fru": "system/chassis/motherboard/regulator2",
905                   "i2c_interface":
906                   {
907                       "bus": 1,
908                       "address": "0x70"
909                   }
910                 }
911               ]
912             }
913         )"_json;
914         parseChassis(element);
915         ADD_FAILURE() << "Should not have reached this line.";
916     }
917     catch (const std::invalid_argument& e)
918     {
919         EXPECT_STREQ(e.what(), "Required property missing: number");
920     }
921 
922     // Test where fails: Element is not an object
923     try
924     {
925         const json element = R"( [ "0xFF", "0x01" ] )"_json;
926         parseChassis(element);
927         ADD_FAILURE() << "Should not have reached this line.";
928     }
929     catch (const std::invalid_argument& e)
930     {
931         EXPECT_STREQ(e.what(), "Element is not an object");
932     }
933 
934     // Test where fails: number value is < 1
935     try
936     {
937         const json element = R"(
938             {
939               "number": 0
940             }
941         )"_json;
942         parseChassis(element);
943         ADD_FAILURE() << "Should not have reached this line.";
944     }
945     catch (const std::invalid_argument& e)
946     {
947         EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0");
948     }
949 
950     // Test where fails: devices value is invalid
951     try
952     {
953         const json element = R"(
954             {
955               "number": 1,
956               "devices": 2
957             }
958         )"_json;
959         parseChassis(element);
960         ADD_FAILURE() << "Should not have reached this line.";
961     }
962     catch (const std::invalid_argument& e)
963     {
964         EXPECT_STREQ(e.what(), "Element is not an array");
965     }
966 }
967 
968 TEST(ConfigFileParserTests, ParseChassisArray)
969 {
970     // Test where works
971     {
972         const json element = R"(
973             [
974               { "number": 1 },
975               { "number": 2 }
976             ]
977         )"_json;
978         std::vector<std::unique_ptr<Chassis>> chassis =
979             parseChassisArray(element);
980         EXPECT_EQ(chassis.size(), 2);
981         EXPECT_EQ(chassis[0]->getNumber(), 1);
982         EXPECT_EQ(chassis[1]->getNumber(), 2);
983     }
984 
985     // Test where fails: Element is not an array
986     try
987     {
988         const json element = R"(
989             {
990               "foo": "bar"
991             }
992         )"_json;
993         parseChassisArray(element);
994         ADD_FAILURE() << "Should not have reached this line.";
995     }
996     catch (const std::invalid_argument& e)
997     {
998         EXPECT_STREQ(e.what(), "Element is not an array");
999     }
1000 }
1001 
1002 TEST(ConfigFileParserTests, ParseComparePresence)
1003 {
1004     // Test where works
1005     {
1006         const json element = R"(
1007             {
1008               "fru": "system/chassis/motherboard/cpu3",
1009               "value": true
1010             }
1011         )"_json;
1012         std::unique_ptr<ComparePresenceAction> action =
1013             parseComparePresence(element);
1014         EXPECT_EQ(
1015             action->getFRU(),
1016             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu3");
1017         EXPECT_EQ(action->getValue(), true);
1018     }
1019 
1020     // Test where fails: Element is not an object
1021     try
1022     {
1023         const json element = R"( [ "0xFF", "0x01" ] )"_json;
1024         parseComparePresence(element);
1025         ADD_FAILURE() << "Should not have reached this line.";
1026     }
1027     catch (const std::invalid_argument& e)
1028     {
1029         EXPECT_STREQ(e.what(), "Element is not an object");
1030     }
1031 
1032     // Test where fails: Invalid property specified
1033     try
1034     {
1035         const json element = R"(
1036             {
1037               "fru": "system/chassis/motherboard/cpu3",
1038               "value": true,
1039               "foo" : true
1040             }
1041         )"_json;
1042         parseComparePresence(element);
1043         ADD_FAILURE() << "Should not have reached this line.";
1044     }
1045     catch (const std::invalid_argument& e)
1046     {
1047         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1048     }
1049 
1050     // Test where fails: Required fru property not specified
1051     try
1052     {
1053         const json element = R"(
1054             {
1055               "value": true
1056             }
1057         )"_json;
1058         parseComparePresence(element);
1059         ADD_FAILURE() << "Should not have reached this line.";
1060     }
1061     catch (const std::invalid_argument& e)
1062     {
1063         EXPECT_STREQ(e.what(), "Required property missing: fru");
1064     }
1065 
1066     // Test where fails: Required value property not specified
1067     try
1068     {
1069         const json element = R"(
1070             {
1071               "fru": "system/chassis/motherboard/cpu3"
1072             }
1073         )"_json;
1074         parseComparePresence(element);
1075         ADD_FAILURE() << "Should not have reached this line.";
1076     }
1077     catch (const std::invalid_argument& e)
1078     {
1079         EXPECT_STREQ(e.what(), "Required property missing: value");
1080     }
1081 
1082     // Test where fails: fru value is invalid
1083     try
1084     {
1085         const json element = R"(
1086             {
1087               "fru": 1,
1088               "value": true
1089             }
1090         )"_json;
1091         parseComparePresence(element);
1092         ADD_FAILURE() << "Should not have reached this line.";
1093     }
1094     catch (const std::invalid_argument& e)
1095     {
1096         EXPECT_STREQ(e.what(), "Element is not a string");
1097     }
1098 
1099     // Test where fails: value value is invalid
1100     try
1101     {
1102         const json element = R"(
1103             {
1104               "fru": "system/chassis/motherboard/cpu3",
1105               "value": 1
1106             }
1107         )"_json;
1108         parseComparePresence(element);
1109         ADD_FAILURE() << "Should not have reached this line.";
1110     }
1111     catch (const std::invalid_argument& e)
1112     {
1113         EXPECT_STREQ(e.what(), "Element is not a boolean");
1114     }
1115 }
1116 
1117 TEST(ConfigFileParserTests, ParseCompareVPD)
1118 {
1119     // Test where works, using "value"
1120     {
1121         const json element = R"(
1122             {
1123               "fru": "system/chassis/disk_backplane",
1124               "keyword": "CCIN",
1125               "value": "2D35"
1126             }
1127         )"_json;
1128         std::unique_ptr<CompareVPDAction> action = parseCompareVPD(element);
1129         EXPECT_EQ(
1130             action->getFRU(),
1131             "/xyz/openbmc_project/inventory/system/chassis/disk_backplane");
1132         EXPECT_EQ(action->getKeyword(), "CCIN");
1133         EXPECT_EQ(action->getValue(),
1134                   (std::vector<uint8_t>{0x32, 0x44, 0x33, 0x35}));
1135     }
1136 
1137     // Test where works, using "byte_values"
1138     {
1139         const json element = R"(
1140             {
1141               "fru": "system/chassis/disk_backplane",
1142               "keyword": "CCIN",
1143               "byte_values": ["0x11", "0x22", "0x33"]
1144             }
1145         )"_json;
1146         std::unique_ptr<CompareVPDAction> action = parseCompareVPD(element);
1147         EXPECT_EQ(
1148             action->getFRU(),
1149             "/xyz/openbmc_project/inventory/system/chassis/disk_backplane");
1150         EXPECT_EQ(action->getKeyword(), "CCIN");
1151         EXPECT_EQ(action->getValue(), (std::vector<uint8_t>{0x11, 0x22, 0x33}));
1152     }
1153 
1154     // Test where fails: Element is not an object
1155     try
1156     {
1157         const json element = R"( [ "0xFF", "0x01" ] )"_json;
1158         parseCompareVPD(element);
1159         ADD_FAILURE() << "Should not have reached this line.";
1160     }
1161     catch (const std::invalid_argument& e)
1162     {
1163         EXPECT_STREQ(e.what(), "Element is not an object");
1164     }
1165 
1166     // Test where fails: Invalid property specified
1167     try
1168     {
1169         const json element = R"(
1170             {
1171               "fru": "system/chassis/disk_backplane",
1172               "keyword": "CCIN",
1173               "value": "2D35",
1174               "foo" : true
1175             }
1176         )"_json;
1177         parseCompareVPD(element);
1178         ADD_FAILURE() << "Should not have reached this line.";
1179     }
1180     catch (const std::invalid_argument& e)
1181     {
1182         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1183     }
1184 
1185     // Test where fails: Required fru property not specified
1186     try
1187     {
1188         const json element = R"(
1189             {
1190               "keyword": "CCIN",
1191               "value": "2D35"
1192             }
1193         )"_json;
1194         parseCompareVPD(element);
1195         ADD_FAILURE() << "Should not have reached this line.";
1196     }
1197     catch (const std::invalid_argument& e)
1198     {
1199         EXPECT_STREQ(e.what(), "Required property missing: fru");
1200     }
1201 
1202     // Test where fails: Required keyword property not specified
1203     try
1204     {
1205         const json element = R"(
1206             {
1207               "fru": "system/chassis/disk_backplane",
1208               "value": "2D35"
1209             }
1210         )"_json;
1211         parseCompareVPD(element);
1212         ADD_FAILURE() << "Should not have reached this line.";
1213     }
1214     catch (const std::invalid_argument& e)
1215     {
1216         EXPECT_STREQ(e.what(), "Required property missing: keyword");
1217     }
1218 
1219     // Test where fails: Required value property not specified
1220     try
1221     {
1222         const json element = R"(
1223             {
1224               "fru": "system/chassis/disk_backplane",
1225               "keyword": "CCIN"
1226             }
1227         )"_json;
1228         parseCompareVPD(element);
1229         ADD_FAILURE() << "Should not have reached this line.";
1230     }
1231     catch (const std::invalid_argument& e)
1232     {
1233         EXPECT_STREQ(e.what(), "Invalid property: Must contain "
1234                                "either value or byte_values");
1235     }
1236 
1237     // Test where fails: both value and byte_value specified
1238     try
1239     {
1240         const json element = R"(
1241             {
1242               "fru": "system/chassis/disk_backplane",
1243               "keyword": "CCIN",
1244               "value": "2D35",
1245               "byte_values": [ "0x01", "0x02" ]
1246             }
1247         )"_json;
1248         parseCompareVPD(element);
1249         ADD_FAILURE() << "Should not have reached this line.";
1250     }
1251     catch (const std::invalid_argument& e)
1252     {
1253         EXPECT_STREQ(e.what(), "Invalid property: Must contain "
1254                                "either value or byte_values");
1255     }
1256 
1257     // Test where fails: fru value is invalid
1258     try
1259     {
1260         const json element = R"(
1261             {
1262               "fru": 1,
1263               "keyword": "CCIN",
1264               "value": "2D35"
1265             }
1266         )"_json;
1267         parseCompareVPD(element);
1268         ADD_FAILURE() << "Should not have reached this line.";
1269     }
1270     catch (const std::invalid_argument& e)
1271     {
1272         EXPECT_STREQ(e.what(), "Element is not a string");
1273     }
1274 
1275     // Test where fails: keyword value is invalid
1276     try
1277     {
1278         const json element = R"(
1279             {
1280               "fru": "system/chassis/disk_backplane",
1281               "keyword": 1,
1282               "value": "2D35"
1283             }
1284         )"_json;
1285         parseCompareVPD(element);
1286         ADD_FAILURE() << "Should not have reached this line.";
1287     }
1288     catch (const std::invalid_argument& e)
1289     {
1290         EXPECT_STREQ(e.what(), "Element is not a string");
1291     }
1292 
1293     // Test where fails: value value is invalid
1294     try
1295     {
1296         const json element = R"(
1297             {
1298               "fru": "system/chassis/disk_backplane",
1299               "keyword": "CCIN",
1300               "value": 1
1301             }
1302         )"_json;
1303         parseCompareVPD(element);
1304         ADD_FAILURE() << "Should not have reached this line.";
1305     }
1306     catch (const std::invalid_argument& e)
1307     {
1308         EXPECT_STREQ(e.what(), "Element is not a string");
1309     }
1310 
1311     // Test where fails: byte_values is wrong format
1312     try
1313     {
1314         const json element = R"(
1315             {
1316               "fru": "system/chassis/disk_backplane",
1317               "keyword": "CCIN",
1318               "byte_values": [1, 2, 3]
1319             }
1320         )"_json;
1321         parseCompareVPD(element);
1322         ADD_FAILURE() << "Should not have reached this line.";
1323     }
1324     catch (const std::invalid_argument& e)
1325     {
1326         EXPECT_STREQ(e.what(), "Element is not a string");
1327     }
1328 }
1329 
1330 TEST(ConfigFileParserTests, ParseConfiguration)
1331 {
1332     // Test where works: actions required property specified
1333     {
1334         const json element = R"(
1335             {
1336               "actions": [
1337                 {
1338                   "pmbus_write_vout_command": {
1339                     "format": "linear"
1340                   }
1341                 }
1342               ]
1343             }
1344         )"_json;
1345         std::unique_ptr<Configuration> configuration =
1346             parseConfiguration(element);
1347         EXPECT_EQ(configuration->getActions().size(), 1);
1348         EXPECT_EQ(configuration->getVolts().has_value(), false);
1349     }
1350 
1351     // Test where works: volts and actions properties specified
1352     {
1353         const json element = R"(
1354             {
1355               "comments": [ "comments property" ],
1356               "volts": 1.03,
1357               "actions": [
1358                 { "pmbus_write_vout_command": { "format": "linear" } },
1359                 { "run_rule": "set_voltage_rule" }
1360               ]
1361             }
1362         )"_json;
1363         std::unique_ptr<Configuration> configuration =
1364             parseConfiguration(element);
1365         EXPECT_EQ(configuration->getVolts().has_value(), true);
1366         EXPECT_EQ(configuration->getVolts().value(), 1.03);
1367         EXPECT_EQ(configuration->getActions().size(), 2);
1368     }
1369 
1370     // Test where works: volts and rule_id properties specified
1371     {
1372         const json element = R"(
1373             {
1374               "volts": 1.05,
1375               "rule_id": "set_voltage_rule"
1376             }
1377         )"_json;
1378         std::unique_ptr<Configuration> configuration =
1379             parseConfiguration(element);
1380         EXPECT_EQ(configuration->getVolts().has_value(), true);
1381         EXPECT_EQ(configuration->getVolts().value(), 1.05);
1382         EXPECT_EQ(configuration->getActions().size(), 1);
1383     }
1384 
1385     // Test where fails: volts value is invalid
1386     try
1387     {
1388         const json element = R"(
1389             {
1390               "volts": "foo",
1391               "actions": [
1392                 {
1393                   "pmbus_write_vout_command": {
1394                     "format": "linear"
1395                   }
1396                 }
1397               ]
1398             }
1399         )"_json;
1400         parseConfiguration(element);
1401         ADD_FAILURE() << "Should not have reached this line.";
1402     }
1403     catch (const std::invalid_argument& e)
1404     {
1405         EXPECT_STREQ(e.what(), "Element is not a number");
1406     }
1407 
1408     // Test where fails: actions object is invalid
1409     try
1410     {
1411         const json element = R"(
1412             {
1413               "volts": 1.03,
1414               "actions": 1
1415             }
1416         )"_json;
1417         parseConfiguration(element);
1418         ADD_FAILURE() << "Should not have reached this line.";
1419     }
1420     catch (const std::invalid_argument& e)
1421     {
1422         EXPECT_STREQ(e.what(), "Element is not an array");
1423     }
1424 
1425     // Test where fails: rule_id value is invalid
1426     try
1427     {
1428         const json element = R"(
1429             {
1430               "volts": 1.05,
1431               "rule_id": 1
1432             }
1433         )"_json;
1434         parseConfiguration(element);
1435         ADD_FAILURE() << "Should not have reached this line.";
1436     }
1437     catch (const std::invalid_argument& e)
1438     {
1439         EXPECT_STREQ(e.what(), "Element is not a string");
1440     }
1441 
1442     // Test where fails: Required actions or rule_id property not specified
1443     try
1444     {
1445         const json element = R"(
1446             {
1447               "volts": 1.03
1448             }
1449         )"_json;
1450         parseConfiguration(element);
1451         ADD_FAILURE() << "Should not have reached this line.";
1452     }
1453     catch (const std::invalid_argument& e)
1454     {
1455         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
1456                                "either rule_id or actions");
1457     }
1458 
1459     // Test where fails: Required actions or rule_id property both specified
1460     try
1461     {
1462         const json element = R"(
1463             {
1464               "volts": 1.03,
1465               "rule_id": "set_voltage_rule",
1466               "actions": [
1467                 {
1468                   "pmbus_write_vout_command": {
1469                     "format": "linear"
1470                   }
1471                 }
1472               ]
1473             }
1474         )"_json;
1475         parseConfiguration(element);
1476         ADD_FAILURE() << "Should not have reached this line.";
1477     }
1478     catch (const std::invalid_argument& e)
1479     {
1480         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
1481                                "either rule_id or actions");
1482     }
1483 
1484     // Test where fails: Element is not an object
1485     try
1486     {
1487         const json element = R"( [ "0xFF", "0x01" ] )"_json;
1488         parseConfiguration(element);
1489         ADD_FAILURE() << "Should not have reached this line.";
1490     }
1491     catch (const std::invalid_argument& e)
1492     {
1493         EXPECT_STREQ(e.what(), "Element is not an object");
1494     }
1495 
1496     // Test where fails: Invalid property specified
1497     try
1498     {
1499         const json element = R"(
1500             {
1501               "volts": 1.03,
1502               "rule_id": "set_voltage_rule",
1503               "foo": 1
1504             }
1505         )"_json;
1506         parseConfiguration(element);
1507         ADD_FAILURE() << "Should not have reached this line.";
1508     }
1509     catch (const std::invalid_argument& e)
1510     {
1511         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1512     }
1513 }
1514 
1515 TEST(ConfigFileParserTests, ParseDevice)
1516 {
1517     // Test where works: Only required properties specified
1518     {
1519         const json element = R"(
1520             {
1521               "id": "vdd_regulator",
1522               "is_regulator": true,
1523               "fru": "system/chassis/motherboard/regulator2",
1524               "i2c_interface": { "bus": 1, "address": "0x70" }
1525             }
1526         )"_json;
1527         std::unique_ptr<Device> device = parseDevice(element);
1528         EXPECT_EQ(device->getID(), "vdd_regulator");
1529         EXPECT_EQ(device->isRegulator(), true);
1530         EXPECT_EQ(device->getFRU(), "/xyz/openbmc_project/inventory/system/"
1531                                     "chassis/motherboard/regulator2");
1532         EXPECT_NE(&(device->getI2CInterface()), nullptr);
1533         EXPECT_EQ(device->getPresenceDetection(), nullptr);
1534         EXPECT_EQ(device->getConfiguration(), nullptr);
1535         EXPECT_EQ(device->getRails().size(), 0);
1536     }
1537 
1538     // Test where works: All properties specified
1539     {
1540         const json element = R"(
1541             {
1542               "id": "vdd_regulator",
1543               "is_regulator": true,
1544               "fru": "system/chassis/motherboard/regulator2",
1545               "i2c_interface":
1546               {
1547                   "bus": 1,
1548                   "address": "0x70"
1549               },
1550               "configuration":
1551               {
1552                   "rule_id": "configure_ir35221_rule"
1553               },
1554               "presence_detection":
1555               {
1556                   "rule_id": "is_foobar_backplane_installed_rule"
1557               },
1558               "rails":
1559               [
1560                 {
1561                   "id": "vdd"
1562                 }
1563               ]
1564             }
1565         )"_json;
1566         std::unique_ptr<Device> device = parseDevice(element);
1567         EXPECT_EQ(device->getID(), "vdd_regulator");
1568         EXPECT_EQ(device->isRegulator(), true);
1569         EXPECT_EQ(device->getFRU(), "/xyz/openbmc_project/inventory/system/"
1570                                     "chassis/motherboard/regulator2");
1571         EXPECT_NE(&(device->getI2CInterface()), nullptr);
1572         EXPECT_NE(device->getPresenceDetection(), nullptr);
1573         EXPECT_NE(device->getConfiguration(), nullptr);
1574         EXPECT_EQ(device->getRails().size(), 1);
1575     }
1576 
1577     // Test where fails: rails property exists and is_regulator is false
1578     try
1579     {
1580         const json element = R"(
1581             {
1582               "id": "vdd_regulator",
1583               "is_regulator": false,
1584               "fru": "system/chassis/motherboard/regulator2",
1585               "i2c_interface":
1586               {
1587                   "bus": 1,
1588                   "address": "0x70"
1589               },
1590               "configuration":
1591               {
1592                   "rule_id": "configure_ir35221_rule"
1593               },
1594               "rails":
1595               [
1596                 {
1597                   "id": "vdd"
1598                 }
1599               ]
1600             }
1601         )"_json;
1602         parseDevice(element);
1603         ADD_FAILURE() << "Should not have reached this line.";
1604     }
1605     catch (const std::invalid_argument& e)
1606     {
1607         EXPECT_STREQ(e.what(),
1608                      "Invalid rails property when is_regulator is false");
1609     }
1610 
1611     // Test where fails: id value is invalid
1612     try
1613     {
1614         const json element = R"(
1615             {
1616               "id": 3,
1617               "is_regulator": true,
1618               "fru": "system/chassis/motherboard/regulator2",
1619               "i2c_interface":
1620               {
1621                   "bus": 1,
1622                   "address": "0x70"
1623               }
1624             }
1625         )"_json;
1626         parseDevice(element);
1627         ADD_FAILURE() << "Should not have reached this line.";
1628     }
1629     catch (const std::invalid_argument& e)
1630     {
1631         EXPECT_STREQ(e.what(), "Element is not a string");
1632     }
1633 
1634     // Test where fails: is_regulator value is invalid
1635     try
1636     {
1637         const json element = R"(
1638             {
1639               "id": "vdd_regulator",
1640               "is_regulator": 3,
1641               "fru": "system/chassis/motherboard/regulator2",
1642               "i2c_interface":
1643               {
1644                   "bus": 1,
1645                   "address": "0x70"
1646               }
1647             }
1648         )"_json;
1649         parseDevice(element);
1650         ADD_FAILURE() << "Should not have reached this line.";
1651     }
1652     catch (const std::invalid_argument& e)
1653     {
1654         EXPECT_STREQ(e.what(), "Element is not a boolean");
1655     }
1656 
1657     // Test where fails: fru value is invalid
1658     try
1659     {
1660         const json element = R"(
1661             {
1662               "id": "vdd_regulator",
1663               "is_regulator": true,
1664               "fru": 2,
1665               "i2c_interface":
1666               {
1667                   "bus": 1,
1668                   "address": "0x70"
1669               }
1670             }
1671         )"_json;
1672         parseDevice(element);
1673         ADD_FAILURE() << "Should not have reached this line.";
1674     }
1675     catch (const std::invalid_argument& e)
1676     {
1677         EXPECT_STREQ(e.what(), "Element is not a string");
1678     }
1679 
1680     // Test where fails: i2c_interface value is invalid
1681     try
1682     {
1683         const json element = R"(
1684             {
1685               "id": "vdd_regulator",
1686               "is_regulator": true,
1687               "fru": "system/chassis/motherboard/regulator2",
1688               "i2c_interface": 3
1689             }
1690         )"_json;
1691         parseDevice(element);
1692         ADD_FAILURE() << "Should not have reached this line.";
1693     }
1694     catch (const std::invalid_argument& e)
1695     {
1696         EXPECT_STREQ(e.what(), "Element is not an object");
1697     }
1698 
1699     // Test where fails: Required id property not specified
1700     try
1701     {
1702         const json element = R"(
1703             {
1704               "is_regulator": true,
1705               "fru": "system/chassis/motherboard/regulator2",
1706               "i2c_interface":
1707               {
1708                   "bus": 1,
1709                   "address": "0x70"
1710               }
1711             }
1712         )"_json;
1713         parseDevice(element);
1714         ADD_FAILURE() << "Should not have reached this line.";
1715     }
1716     catch (const std::invalid_argument& e)
1717     {
1718         EXPECT_STREQ(e.what(), "Required property missing: id");
1719     }
1720 
1721     // Test where fails: Required is_regulator property not specified
1722     try
1723     {
1724         const json element = R"(
1725             {
1726               "id": "vdd_regulator",
1727               "fru": "system/chassis/motherboard/regulator2",
1728               "i2c_interface":
1729               {
1730                   "bus": 1,
1731                   "address": "0x70"
1732               }
1733             }
1734         )"_json;
1735         parseDevice(element);
1736         ADD_FAILURE() << "Should not have reached this line.";
1737     }
1738     catch (const std::invalid_argument& e)
1739     {
1740         EXPECT_STREQ(e.what(), "Required property missing: is_regulator");
1741     }
1742 
1743     // Test where fails: Required fru property not specified
1744     try
1745     {
1746         const json element = R"(
1747             {
1748               "id": "vdd_regulator",
1749               "is_regulator": true,
1750               "i2c_interface":
1751               {
1752                   "bus": 1,
1753                   "address": "0x70"
1754               }
1755             }
1756         )"_json;
1757         parseDevice(element);
1758         ADD_FAILURE() << "Should not have reached this line.";
1759     }
1760     catch (const std::invalid_argument& e)
1761     {
1762         EXPECT_STREQ(e.what(), "Required property missing: fru");
1763     }
1764 
1765     // Test where fails: Required i2c_interface property not specified
1766     try
1767     {
1768         const json element = R"(
1769             {
1770               "id": "vdd_regulator",
1771               "is_regulator": true,
1772               "fru": "system/chassis/motherboard/regulator2"
1773             }
1774         )"_json;
1775         parseDevice(element);
1776         ADD_FAILURE() << "Should not have reached this line.";
1777     }
1778     catch (const std::invalid_argument& e)
1779     {
1780         EXPECT_STREQ(e.what(), "Required property missing: i2c_interface");
1781     }
1782 
1783     // Test where fails: Element is not an object
1784     try
1785     {
1786         const json element = R"( [ "0xFF", "0x01" ] )"_json;
1787         parseDevice(element);
1788         ADD_FAILURE() << "Should not have reached this line.";
1789     }
1790     catch (const std::invalid_argument& e)
1791     {
1792         EXPECT_STREQ(e.what(), "Element is not an object");
1793     }
1794 
1795     // Test where fails: Invalid property specified
1796     try
1797     {
1798         const json element = R"(
1799             {
1800               "id": "vdd_regulator",
1801               "is_regulator": true,
1802               "fru": "system/chassis/motherboard/regulator2",
1803               "i2c_interface": { "bus": 1, "address": "0x70" },
1804               "foo" : true
1805             }
1806         )"_json;
1807         parseDevice(element);
1808         ADD_FAILURE() << "Should not have reached this line.";
1809     }
1810     catch (const std::invalid_argument& e)
1811     {
1812         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1813     }
1814 }
1815 
1816 TEST(ConfigFileParserTests, ParseDeviceArray)
1817 {
1818     // Test where works
1819     {
1820         const json element = R"(
1821             [
1822               {
1823                 "id": "vdd_regulator",
1824                 "is_regulator": true,
1825                 "fru": "system/chassis/motherboard/regulator2",
1826                 "i2c_interface": { "bus": 1, "address": "0x70" }
1827               },
1828               {
1829                 "id": "vio_regulator",
1830                 "is_regulator": true,
1831                 "fru": "system/chassis/motherboard/regulator2",
1832                 "i2c_interface": { "bus": 1, "address": "0x71" }
1833               }
1834             ]
1835         )"_json;
1836         std::vector<std::unique_ptr<Device>> devices =
1837             parseDeviceArray(element);
1838         EXPECT_EQ(devices.size(), 2);
1839         EXPECT_EQ(devices[0]->getID(), "vdd_regulator");
1840         EXPECT_EQ(devices[1]->getID(), "vio_regulator");
1841     }
1842 
1843     // Test where fails: Element is not an array
1844     try
1845     {
1846         const json element = R"(
1847             {
1848               "foo": "bar"
1849             }
1850         )"_json;
1851         parseDeviceArray(element);
1852         ADD_FAILURE() << "Should not have reached this line.";
1853     }
1854     catch (const std::invalid_argument& e)
1855     {
1856         EXPECT_STREQ(e.what(), "Element is not an array");
1857     }
1858 }
1859 
1860 TEST(ConfigFileParserTests, ParseDouble)
1861 {
1862     // Test where works: floating point value
1863     {
1864         const json element = R"( 1.03 )"_json;
1865         double value = parseDouble(element);
1866         EXPECT_EQ(value, 1.03);
1867     }
1868 
1869     // Test where works: integer value
1870     {
1871         const json element = R"( 24 )"_json;
1872         double value = parseDouble(element);
1873         EXPECT_EQ(value, 24.0);
1874     }
1875 
1876     // Test where fails: Element is not a number
1877     try
1878     {
1879         const json element = R"( true )"_json;
1880         parseDouble(element);
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 number");
1886     }
1887 }
1888 
1889 TEST(ConfigFileParserTests, ParseHexByte)
1890 {
1891     // Test where works: "0xFF"
1892     {
1893         const json element = R"( "0xFF" )"_json;
1894         uint8_t value = parseHexByte(element);
1895         EXPECT_EQ(value, 0xFF);
1896     }
1897 
1898     // Test where works: "0xff"
1899     {
1900         const json element = R"( "0xff" )"_json;
1901         uint8_t value = parseHexByte(element);
1902         EXPECT_EQ(value, 0xff);
1903     }
1904 
1905     // Test where works: "0xf"
1906     {
1907         const json element = R"( "0xf" )"_json;
1908         uint8_t value = parseHexByte(element);
1909         EXPECT_EQ(value, 0xf);
1910     }
1911 
1912     // Test where fails: "0xfff"
1913     try
1914     {
1915         const json element = R"( "0xfff" )"_json;
1916         parseHexByte(element);
1917         ADD_FAILURE() << "Should not have reached this line.";
1918     }
1919     catch (const std::invalid_argument& e)
1920     {
1921         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1922     }
1923 
1924     // Test where fails: "0xAG"
1925     try
1926     {
1927         const json element = R"( "0xAG" )"_json;
1928         parseHexByte(element);
1929         ADD_FAILURE() << "Should not have reached this line.";
1930     }
1931     catch (const std::invalid_argument& e)
1932     {
1933         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1934     }
1935 
1936     // Test where fails: "ff"
1937     try
1938     {
1939         const json element = R"( "ff" )"_json;
1940         parseHexByte(element);
1941         ADD_FAILURE() << "Should not have reached this line.";
1942     }
1943     catch (const std::invalid_argument& e)
1944     {
1945         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1946     }
1947 
1948     // Test where fails: ""
1949     try
1950     {
1951         const json element = "";
1952         parseHexByte(element);
1953         ADD_FAILURE() << "Should not have reached this line.";
1954     }
1955     catch (const std::invalid_argument& e)
1956     {
1957         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1958     }
1959 
1960     // Test where fails: "f"
1961     try
1962     {
1963         const json element = R"( "f" )"_json;
1964         parseHexByte(element);
1965         ADD_FAILURE() << "Should not have reached this line.";
1966     }
1967     catch (const std::invalid_argument& e)
1968     {
1969         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1970     }
1971 
1972     // Test where fails: "0x"
1973     try
1974     {
1975         const json element = R"( "0x" )"_json;
1976         parseHexByte(element);
1977         ADD_FAILURE() << "Should not have reached this line.";
1978     }
1979     catch (const std::invalid_argument& e)
1980     {
1981         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1982     }
1983 
1984     // Test where fails: "0Xff"
1985     try
1986     {
1987         const json element = R"( "0XFF" )"_json;
1988         parseHexByte(element);
1989         ADD_FAILURE() << "Should not have reached this line.";
1990     }
1991     catch (const std::invalid_argument& e)
1992     {
1993         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1994     }
1995 }
1996 
1997 TEST(ConfigFileParserTests, ParseHexByteArray)
1998 {
1999     // Test where works
2000     {
2001         const json element = R"( [ "0xCC", "0xFF" ] )"_json;
2002         std::vector<uint8_t> hexBytes = parseHexByteArray(element);
2003         std::vector<uint8_t> expected = {0xcc, 0xff};
2004         EXPECT_EQ(hexBytes, expected);
2005     }
2006 
2007     // Test where fails: Element is not an array
2008     try
2009     {
2010         const json element = 0;
2011         parseHexByteArray(element);
2012         ADD_FAILURE() << "Should not have reached this line.";
2013     }
2014     catch (const std::invalid_argument& e)
2015     {
2016         EXPECT_STREQ(e.what(), "Element is not an array");
2017     }
2018 }
2019 
2020 TEST(ConfigFileParserTests, ParseI2CCaptureBytes)
2021 {
2022     // Test where works
2023     {
2024         const json element = R"(
2025             {
2026               "register": "0xA0",
2027               "count": 2
2028             }
2029         )"_json;
2030         std::unique_ptr<I2CCaptureBytesAction> action =
2031             parseI2CCaptureBytes(element);
2032         EXPECT_EQ(action->getRegister(), 0xA0);
2033         EXPECT_EQ(action->getCount(), 2);
2034     }
2035 
2036     // Test where fails: Element is not an object
2037     try
2038     {
2039         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2040         parseI2CCaptureBytes(element);
2041         ADD_FAILURE() << "Should not have reached this line.";
2042     }
2043     catch (const std::invalid_argument& e)
2044     {
2045         EXPECT_STREQ(e.what(), "Element is not an object");
2046     }
2047 
2048     // Test where fails: register value is invalid
2049     try
2050     {
2051         const json element = R"(
2052             {
2053               "register": "0x0Z",
2054               "count": 2
2055             }
2056         )"_json;
2057         parseI2CCaptureBytes(element);
2058         ADD_FAILURE() << "Should not have reached this line.";
2059     }
2060     catch (const std::invalid_argument& e)
2061     {
2062         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2063     }
2064 
2065     // Test where fails: count value is invalid
2066     try
2067     {
2068         const json element = R"(
2069             {
2070               "register": "0xA0",
2071               "count": 0
2072             }
2073         )"_json;
2074         parseI2CCaptureBytes(element);
2075         ADD_FAILURE() << "Should not have reached this line.";
2076     }
2077     catch (const std::invalid_argument& e)
2078     {
2079         EXPECT_STREQ(e.what(), "Invalid byte count: Must be > 0");
2080     }
2081 
2082     // Test where fails: Required register property not specified
2083     try
2084     {
2085         const json element = R"(
2086             {
2087               "count": 2
2088             }
2089         )"_json;
2090         parseI2CCaptureBytes(element);
2091         ADD_FAILURE() << "Should not have reached this line.";
2092     }
2093     catch (const std::invalid_argument& e)
2094     {
2095         EXPECT_STREQ(e.what(), "Required property missing: register");
2096     }
2097 
2098     // Test where fails: Required count property not specified
2099     try
2100     {
2101         const json element = R"(
2102             {
2103               "register": "0xA0"
2104             }
2105         )"_json;
2106         parseI2CCaptureBytes(element);
2107         ADD_FAILURE() << "Should not have reached this line.";
2108     }
2109     catch (const std::invalid_argument& e)
2110     {
2111         EXPECT_STREQ(e.what(), "Required property missing: count");
2112     }
2113 
2114     // Test where fails: Invalid property specified
2115     try
2116     {
2117         const json element = R"(
2118             {
2119               "register": "0xA0",
2120               "count": 2,
2121               "foo": 3
2122             }
2123         )"_json;
2124         parseI2CCaptureBytes(element);
2125         ADD_FAILURE() << "Should not have reached this line.";
2126     }
2127     catch (const std::invalid_argument& e)
2128     {
2129         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2130     }
2131 }
2132 
2133 TEST(ConfigFileParserTests, ParseI2CCompareBit)
2134 {
2135     // Test where works
2136     {
2137         const json element = R"(
2138             {
2139               "register": "0xA0",
2140               "position": 3,
2141               "value": 0
2142             }
2143         )"_json;
2144         std::unique_ptr<I2CCompareBitAction> action =
2145             parseI2CCompareBit(element);
2146         EXPECT_EQ(action->getRegister(), 0xA0);
2147         EXPECT_EQ(action->getPosition(), 3);
2148         EXPECT_EQ(action->getValue(), 0);
2149     }
2150 
2151     // Test where fails: Invalid property specified
2152     try
2153     {
2154         const json element = R"(
2155             {
2156               "register": "0xA0",
2157               "position": 3,
2158               "value": 0,
2159               "foo": 3
2160             }
2161         )"_json;
2162         parseI2CCompareBit(element);
2163         ADD_FAILURE() << "Should not have reached this line.";
2164     }
2165     catch (const std::invalid_argument& e)
2166     {
2167         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2168     }
2169 
2170     // Test where fails: Element is not an object
2171     try
2172     {
2173         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2174         parseI2CCompareBit(element);
2175         ADD_FAILURE() << "Should not have reached this line.";
2176     }
2177     catch (const std::invalid_argument& e)
2178     {
2179         EXPECT_STREQ(e.what(), "Element is not an object");
2180     }
2181 
2182     // Test where fails: register value is invalid
2183     try
2184     {
2185         const json element = R"(
2186             {
2187               "register": "0xAG",
2188               "position": 3,
2189               "value": 0
2190             }
2191         )"_json;
2192         parseI2CCompareBit(element);
2193         ADD_FAILURE() << "Should not have reached this line.";
2194     }
2195     catch (const std::invalid_argument& e)
2196     {
2197         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2198     }
2199 
2200     // Test where fails: position value is invalid
2201     try
2202     {
2203         const json element = R"(
2204                 {
2205                   "register": "0xA0",
2206                   "position": 8,
2207                   "value": 0
2208                 }
2209             )"_json;
2210         parseI2CCompareBit(element);
2211         ADD_FAILURE() << "Should not have reached this line.";
2212     }
2213     catch (const std::invalid_argument& e)
2214     {
2215         EXPECT_STREQ(e.what(), "Element is not a bit position");
2216     }
2217 
2218     // Test where fails: value value is invalid
2219     try
2220     {
2221         const json element = R"(
2222                 {
2223                   "register": "0xA0",
2224                   "position": 3,
2225                   "value": 2
2226                 }
2227             )"_json;
2228         parseI2CCompareBit(element);
2229         ADD_FAILURE() << "Should not have reached this line.";
2230     }
2231     catch (const std::invalid_argument& e)
2232     {
2233         EXPECT_STREQ(e.what(), "Element is not a bit value");
2234     }
2235 
2236     // Test where fails: Required register property not specified
2237     try
2238     {
2239         const json element = R"(
2240             {
2241               "position": 3,
2242               "value": 0
2243             }
2244         )"_json;
2245         parseI2CCompareBit(element);
2246         ADD_FAILURE() << "Should not have reached this line.";
2247     }
2248     catch (const std::invalid_argument& e)
2249     {
2250         EXPECT_STREQ(e.what(), "Required property missing: register");
2251     }
2252 
2253     // Test where fails: Required position property not specified
2254     try
2255     {
2256         const json element = R"(
2257             {
2258               "register": "0xA0",
2259               "value": 0
2260             }
2261         )"_json;
2262         parseI2CCompareBit(element);
2263         ADD_FAILURE() << "Should not have reached this line.";
2264     }
2265     catch (const std::invalid_argument& e)
2266     {
2267         EXPECT_STREQ(e.what(), "Required property missing: position");
2268     }
2269 
2270     // Test where fails: Required value property not specified
2271     try
2272     {
2273         const json element = R"(
2274             {
2275               "register": "0xA0",
2276               "position": 3
2277             }
2278         )"_json;
2279         parseI2CCompareBit(element);
2280         ADD_FAILURE() << "Should not have reached this line.";
2281     }
2282     catch (const std::invalid_argument& e)
2283     {
2284         EXPECT_STREQ(e.what(), "Required property missing: value");
2285     }
2286 }
2287 
2288 TEST(ConfigFileParserTests, ParseI2CCompareByte)
2289 {
2290     // Test where works: Only required properties specified
2291     {
2292         const json element = R"(
2293             {
2294               "register": "0x0A",
2295               "value": "0xCC"
2296             }
2297         )"_json;
2298         std::unique_ptr<I2CCompareByteAction> action =
2299             parseI2CCompareByte(element);
2300         EXPECT_EQ(action->getRegister(), 0x0A);
2301         EXPECT_EQ(action->getValue(), 0xCC);
2302         EXPECT_EQ(action->getMask(), 0xFF);
2303     }
2304 
2305     // Test where works: All properties specified
2306     {
2307         const json element = R"(
2308             {
2309               "register": "0x0A",
2310               "value": "0xCC",
2311               "mask": "0xF7"
2312             }
2313         )"_json;
2314         std::unique_ptr<I2CCompareByteAction> action =
2315             parseI2CCompareByte(element);
2316         EXPECT_EQ(action->getRegister(), 0x0A);
2317         EXPECT_EQ(action->getValue(), 0xCC);
2318         EXPECT_EQ(action->getMask(), 0xF7);
2319     }
2320 
2321     // Test where fails: Element is not an object
2322     try
2323     {
2324         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2325         parseI2CCompareByte(element);
2326         ADD_FAILURE() << "Should not have reached this line.";
2327     }
2328     catch (const std::invalid_argument& e)
2329     {
2330         EXPECT_STREQ(e.what(), "Element is not an object");
2331     }
2332 
2333     // Test where fails: Invalid property specified
2334     try
2335     {
2336         const json element = R"(
2337             {
2338               "register": "0x0A",
2339               "value": "0xCC",
2340               "mask": "0xF7",
2341               "foo": 1
2342             }
2343         )"_json;
2344         parseI2CCompareByte(element);
2345         ADD_FAILURE() << "Should not have reached this line.";
2346     }
2347     catch (const std::invalid_argument& e)
2348     {
2349         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2350     }
2351 
2352     // Test where fails: register value is invalid
2353     try
2354     {
2355         const json element = R"(
2356             {
2357               "register": "0x0Z",
2358               "value": "0xCC",
2359               "mask": "0xF7"
2360             }
2361         )"_json;
2362         parseI2CCompareByte(element);
2363         ADD_FAILURE() << "Should not have reached this line.";
2364     }
2365     catch (const std::invalid_argument& e)
2366     {
2367         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2368     }
2369 
2370     // Test where fails: value value is invalid
2371     try
2372     {
2373         const json element = R"(
2374             {
2375               "register": "0x0A",
2376               "value": "0xCCC",
2377               "mask": "0xF7"
2378             }
2379         )"_json;
2380         parseI2CCompareByte(element);
2381         ADD_FAILURE() << "Should not have reached this line.";
2382     }
2383     catch (const std::invalid_argument& e)
2384     {
2385         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2386     }
2387 
2388     // Test where fails: mask value is invalid
2389     try
2390     {
2391         const json element = R"(
2392             {
2393               "register": "0x0A",
2394               "value": "0xCC",
2395               "mask": "F7"
2396             }
2397         )"_json;
2398         parseI2CCompareByte(element);
2399         ADD_FAILURE() << "Should not have reached this line.";
2400     }
2401     catch (const std::invalid_argument& e)
2402     {
2403         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2404     }
2405 
2406     // Test where fails: Required register property not specified
2407     try
2408     {
2409         const json element = R"(
2410             {
2411               "value": "0xCC",
2412               "mask": "0xF7"
2413             }
2414         )"_json;
2415         parseI2CCompareByte(element);
2416         ADD_FAILURE() << "Should not have reached this line.";
2417     }
2418     catch (const std::invalid_argument& e)
2419     {
2420         EXPECT_STREQ(e.what(), "Required property missing: register");
2421     }
2422 
2423     // Test where fails: Required value property not specified
2424     try
2425     {
2426         const json element = R"(
2427             {
2428               "register": "0x0A",
2429               "mask": "0xF7"
2430             }
2431         )"_json;
2432         parseI2CCompareByte(element);
2433         ADD_FAILURE() << "Should not have reached this line.";
2434     }
2435     catch (const std::invalid_argument& e)
2436     {
2437         EXPECT_STREQ(e.what(), "Required property missing: value");
2438     }
2439 }
2440 
2441 TEST(ConfigFileParserTests, ParseI2CCompareBytes)
2442 {
2443     // Test where works: Only required properties specified
2444     {
2445         const json element = R"(
2446             {
2447               "register": "0x0A",
2448               "values": [ "0xCC", "0xFF" ]
2449             }
2450         )"_json;
2451         std::unique_ptr<I2CCompareBytesAction> action =
2452             parseI2CCompareBytes(element);
2453         EXPECT_EQ(action->getRegister(), 0x0A);
2454         EXPECT_EQ(action->getValues().size(), 2);
2455         EXPECT_EQ(action->getValues()[0], 0xCC);
2456         EXPECT_EQ(action->getValues()[1], 0xFF);
2457         EXPECT_EQ(action->getMasks().size(), 2);
2458         EXPECT_EQ(action->getMasks()[0], 0xFF);
2459         EXPECT_EQ(action->getMasks()[1], 0xFF);
2460     }
2461 
2462     // Test where works: All properties specified
2463     {
2464         const json element = R"(
2465             {
2466               "register": "0x0A",
2467               "values": [ "0xCC", "0xFF" ],
2468               "masks":  [ "0x7F", "0x77" ]
2469             }
2470         )"_json;
2471         std::unique_ptr<I2CCompareBytesAction> action =
2472             parseI2CCompareBytes(element);
2473         EXPECT_EQ(action->getRegister(), 0x0A);
2474         EXPECT_EQ(action->getValues().size(), 2);
2475         EXPECT_EQ(action->getValues()[0], 0xCC);
2476         EXPECT_EQ(action->getValues()[1], 0xFF);
2477         EXPECT_EQ(action->getMasks().size(), 2);
2478         EXPECT_EQ(action->getMasks()[0], 0x7F);
2479         EXPECT_EQ(action->getMasks()[1], 0x77);
2480     }
2481 
2482     // Test where fails: Element is not an object
2483     try
2484     {
2485         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2486         parseI2CCompareBytes(element);
2487         ADD_FAILURE() << "Should not have reached this line.";
2488     }
2489     catch (const std::invalid_argument& e)
2490     {
2491         EXPECT_STREQ(e.what(), "Element is not an object");
2492     }
2493 
2494     // Test where fails: Invalid property specified
2495     try
2496     {
2497         const json element = R"(
2498             {
2499               "register": "0x0A",
2500               "values": [ "0xCC", "0xFF" ],
2501               "masks":  [ "0x7F", "0x7F" ],
2502               "foo": 1
2503             }
2504         )"_json;
2505         parseI2CCompareBytes(element);
2506         ADD_FAILURE() << "Should not have reached this line.";
2507     }
2508     catch (const std::invalid_argument& e)
2509     {
2510         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2511     }
2512 
2513     // Test where fails: register value is invalid
2514     try
2515     {
2516         const json element = R"(
2517             {
2518               "register": "0x0Z",
2519               "values": [ "0xCC", "0xFF" ],
2520               "masks":  [ "0x7F", "0x7F" ]
2521             }
2522         )"_json;
2523         parseI2CCompareBytes(element);
2524         ADD_FAILURE() << "Should not have reached this line.";
2525     }
2526     catch (const std::invalid_argument& e)
2527     {
2528         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2529     }
2530 
2531     // Test where fails: values value is invalid
2532     try
2533     {
2534         const json element = R"(
2535             {
2536               "register": "0x0A",
2537               "values": [ "0xCCC", "0xFF" ],
2538               "masks":  [ "0x7F", "0x7F" ]
2539             }
2540         )"_json;
2541         parseI2CCompareBytes(element);
2542         ADD_FAILURE() << "Should not have reached this line.";
2543     }
2544     catch (const std::invalid_argument& e)
2545     {
2546         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2547     }
2548 
2549     // Test where fails: masks value is invalid
2550     try
2551     {
2552         const json element = R"(
2553             {
2554               "register": "0x0A",
2555               "values": [ "0xCC", "0xFF" ],
2556               "masks":  [ "F", "0x7F" ]
2557             }
2558         )"_json;
2559         parseI2CCompareBytes(element);
2560         ADD_FAILURE() << "Should not have reached this line.";
2561     }
2562     catch (const std::invalid_argument& e)
2563     {
2564         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2565     }
2566 
2567     // Test where fails: number of elements in masks is invalid
2568     try
2569     {
2570         const json element = R"(
2571             {
2572               "register": "0x0A",
2573               "values": [ "0xCC", "0xFF" ],
2574               "masks":  [ "0x7F" ]
2575             }
2576         )"_json;
2577         parseI2CCompareBytes(element);
2578         ADD_FAILURE() << "Should not have reached this line.";
2579     }
2580     catch (const std::invalid_argument& e)
2581     {
2582         EXPECT_STREQ(e.what(), "Invalid number of elements in masks");
2583     }
2584 
2585     // Test where fails: Required register property not specified
2586     try
2587     {
2588         const json element = R"(
2589             {
2590               "values": [ "0xCC", "0xFF" ]
2591             }
2592         )"_json;
2593         parseI2CCompareBytes(element);
2594         ADD_FAILURE() << "Should not have reached this line.";
2595     }
2596     catch (const std::invalid_argument& e)
2597     {
2598         EXPECT_STREQ(e.what(), "Required property missing: register");
2599     }
2600 
2601     // Test where fails: Required values property not specified
2602     try
2603     {
2604         const json element = R"(
2605             {
2606               "register": "0x0A"
2607             }
2608         )"_json;
2609         parseI2CCompareBytes(element);
2610         ADD_FAILURE() << "Should not have reached this line.";
2611     }
2612     catch (const std::invalid_argument& e)
2613     {
2614         EXPECT_STREQ(e.what(), "Required property missing: values");
2615     }
2616 }
2617 
2618 TEST(ConfigFileParserTests, ParseI2CWriteBit)
2619 {
2620     // Test where works
2621     {
2622         const json element = R"(
2623             {
2624               "register": "0xA0",
2625               "position": 3,
2626               "value": 0
2627             }
2628         )"_json;
2629         std::unique_ptr<I2CWriteBitAction> action = parseI2CWriteBit(element);
2630         EXPECT_EQ(action->getRegister(), 0xA0);
2631         EXPECT_EQ(action->getPosition(), 3);
2632         EXPECT_EQ(action->getValue(), 0);
2633     }
2634 
2635     // Test where fails: Invalid property specified
2636     try
2637     {
2638         const json element = R"(
2639             {
2640               "register": "0xA0",
2641               "position": 3,
2642               "value": 0,
2643               "foo": 3
2644             }
2645         )"_json;
2646         parseI2CWriteBit(element);
2647         ADD_FAILURE() << "Should not have reached this line.";
2648     }
2649     catch (const std::invalid_argument& e)
2650     {
2651         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2652     }
2653 
2654     // Test where fails: Element is not an object
2655     try
2656     {
2657         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2658         parseI2CWriteBit(element);
2659         ADD_FAILURE() << "Should not have reached this line.";
2660     }
2661     catch (const std::invalid_argument& e)
2662     {
2663         EXPECT_STREQ(e.what(), "Element is not an object");
2664     }
2665 
2666     // Test where fails: register value is invalid
2667     try
2668     {
2669         const json element = R"(
2670             {
2671               "register": "0xAG",
2672               "position": 3,
2673               "value": 0
2674             }
2675         )"_json;
2676         parseI2CWriteBit(element);
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 hexadecimal string");
2682     }
2683 
2684     // Test where fails: position value is invalid
2685     try
2686     {
2687         const json element = R"(
2688                 {
2689                   "register": "0xA0",
2690                   "position": 8,
2691                   "value": 0
2692                 }
2693             )"_json;
2694         parseI2CWriteBit(element);
2695         ADD_FAILURE() << "Should not have reached this line.";
2696     }
2697     catch (const std::invalid_argument& e)
2698     {
2699         EXPECT_STREQ(e.what(), "Element is not a bit position");
2700     }
2701 
2702     // Test where fails: value value is invalid
2703     try
2704     {
2705         const json element = R"(
2706                 {
2707                   "register": "0xA0",
2708                   "position": 3,
2709                   "value": 2
2710                 }
2711             )"_json;
2712         parseI2CWriteBit(element);
2713         ADD_FAILURE() << "Should not have reached this line.";
2714     }
2715     catch (const std::invalid_argument& e)
2716     {
2717         EXPECT_STREQ(e.what(), "Element is not a bit value");
2718     }
2719 
2720     // Test where fails: Required register property not specified
2721     try
2722     {
2723         const json element = R"(
2724             {
2725               "position": 3,
2726               "value": 0
2727             }
2728         )"_json;
2729         parseI2CWriteBit(element);
2730         ADD_FAILURE() << "Should not have reached this line.";
2731     }
2732     catch (const std::invalid_argument& e)
2733     {
2734         EXPECT_STREQ(e.what(), "Required property missing: register");
2735     }
2736 
2737     // Test where fails: Required position property not specified
2738     try
2739     {
2740         const json element = R"(
2741             {
2742               "register": "0xA0",
2743               "value": 0
2744             }
2745         )"_json;
2746         parseI2CWriteBit(element);
2747         ADD_FAILURE() << "Should not have reached this line.";
2748     }
2749     catch (const std::invalid_argument& e)
2750     {
2751         EXPECT_STREQ(e.what(), "Required property missing: position");
2752     }
2753 
2754     // Test where fails: Required value property not specified
2755     try
2756     {
2757         const json element = R"(
2758             {
2759               "register": "0xA0",
2760               "position": 3
2761             }
2762         )"_json;
2763         parseI2CWriteBit(element);
2764         ADD_FAILURE() << "Should not have reached this line.";
2765     }
2766     catch (const std::invalid_argument& e)
2767     {
2768         EXPECT_STREQ(e.what(), "Required property missing: value");
2769     }
2770 }
2771 
2772 TEST(ConfigFileParserTests, ParseI2CWriteByte)
2773 {
2774     // Test where works: Only required properties specified
2775     {
2776         const json element = R"(
2777             {
2778               "register": "0x0A",
2779               "value": "0xCC"
2780             }
2781         )"_json;
2782         std::unique_ptr<I2CWriteByteAction> action = parseI2CWriteByte(element);
2783         EXPECT_EQ(action->getRegister(), 0x0A);
2784         EXPECT_EQ(action->getValue(), 0xCC);
2785         EXPECT_EQ(action->getMask(), 0xFF);
2786     }
2787 
2788     // Test where works: All properties specified
2789     {
2790         const json element = R"(
2791             {
2792               "register": "0x0A",
2793               "value": "0xCC",
2794               "mask": "0xF7"
2795             }
2796         )"_json;
2797         std::unique_ptr<I2CWriteByteAction> action = parseI2CWriteByte(element);
2798         EXPECT_EQ(action->getRegister(), 0x0A);
2799         EXPECT_EQ(action->getValue(), 0xCC);
2800         EXPECT_EQ(action->getMask(), 0xF7);
2801     }
2802 
2803     // Test where fails: Element is not an object
2804     try
2805     {
2806         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2807         parseI2CWriteByte(element);
2808         ADD_FAILURE() << "Should not have reached this line.";
2809     }
2810     catch (const std::invalid_argument& e)
2811     {
2812         EXPECT_STREQ(e.what(), "Element is not an object");
2813     }
2814 
2815     // Test where fails: Invalid property specified
2816     try
2817     {
2818         const json element = R"(
2819             {
2820               "register": "0x0A",
2821               "value": "0xCC",
2822               "mask": "0xF7",
2823               "foo": 1
2824             }
2825         )"_json;
2826         parseI2CWriteByte(element);
2827         ADD_FAILURE() << "Should not have reached this line.";
2828     }
2829     catch (const std::invalid_argument& e)
2830     {
2831         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2832     }
2833 
2834     // Test where fails: register value is invalid
2835     try
2836     {
2837         const json element = R"(
2838             {
2839               "register": "0x0Z",
2840               "value": "0xCC",
2841               "mask": "0xF7"
2842             }
2843         )"_json;
2844         parseI2CWriteByte(element);
2845         ADD_FAILURE() << "Should not have reached this line.";
2846     }
2847     catch (const std::invalid_argument& e)
2848     {
2849         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2850     }
2851 
2852     // Test where fails: value value is invalid
2853     try
2854     {
2855         const json element = R"(
2856             {
2857               "register": "0x0A",
2858               "value": "0xCCC",
2859               "mask": "0xF7"
2860             }
2861         )"_json;
2862         parseI2CWriteByte(element);
2863         ADD_FAILURE() << "Should not have reached this line.";
2864     }
2865     catch (const std::invalid_argument& e)
2866     {
2867         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2868     }
2869 
2870     // Test where fails: mask value is invalid
2871     try
2872     {
2873         const json element = R"(
2874             {
2875               "register": "0x0A",
2876               "value": "0xCC",
2877               "mask": "F7"
2878             }
2879         )"_json;
2880         parseI2CWriteByte(element);
2881         ADD_FAILURE() << "Should not have reached this line.";
2882     }
2883     catch (const std::invalid_argument& e)
2884     {
2885         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2886     }
2887 
2888     // Test where fails: Required register property not specified
2889     try
2890     {
2891         const json element = R"(
2892             {
2893               "value": "0xCC",
2894               "mask": "0xF7"
2895             }
2896         )"_json;
2897         parseI2CWriteByte(element);
2898         ADD_FAILURE() << "Should not have reached this line.";
2899     }
2900     catch (const std::invalid_argument& e)
2901     {
2902         EXPECT_STREQ(e.what(), "Required property missing: register");
2903     }
2904 
2905     // Test where fails: Required value property not specified
2906     try
2907     {
2908         const json element = R"(
2909             {
2910               "register": "0x0A",
2911               "mask": "0xF7"
2912             }
2913         )"_json;
2914         parseI2CWriteByte(element);
2915         ADD_FAILURE() << "Should not have reached this line.";
2916     }
2917     catch (const std::invalid_argument& e)
2918     {
2919         EXPECT_STREQ(e.what(), "Required property missing: value");
2920     }
2921 }
2922 
2923 TEST(ConfigFileParserTests, ParseI2CWriteBytes)
2924 {
2925     // Test where works: Only required properties specified
2926     {
2927         const json element = R"(
2928             {
2929               "register": "0x0A",
2930               "values": [ "0xCC", "0xFF" ]
2931             }
2932         )"_json;
2933         std::unique_ptr<I2CWriteBytesAction> action =
2934             parseI2CWriteBytes(element);
2935         EXPECT_EQ(action->getRegister(), 0x0A);
2936         EXPECT_EQ(action->getValues().size(), 2);
2937         EXPECT_EQ(action->getValues()[0], 0xCC);
2938         EXPECT_EQ(action->getValues()[1], 0xFF);
2939         EXPECT_EQ(action->getMasks().size(), 0);
2940     }
2941 
2942     // Test where works: All properties specified
2943     {
2944         const json element = R"(
2945             {
2946               "register": "0x0A",
2947               "values": [ "0xCC", "0xFF" ],
2948               "masks":  [ "0x7F", "0x77" ]
2949             }
2950         )"_json;
2951         std::unique_ptr<I2CWriteBytesAction> action =
2952             parseI2CWriteBytes(element);
2953         EXPECT_EQ(action->getRegister(), 0x0A);
2954         EXPECT_EQ(action->getValues().size(), 2);
2955         EXPECT_EQ(action->getValues()[0], 0xCC);
2956         EXPECT_EQ(action->getValues()[1], 0xFF);
2957         EXPECT_EQ(action->getMasks().size(), 2);
2958         EXPECT_EQ(action->getMasks()[0], 0x7F);
2959         EXPECT_EQ(action->getMasks()[1], 0x77);
2960     }
2961 
2962     // Test where fails: Element is not an object
2963     try
2964     {
2965         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2966         parseI2CWriteBytes(element);
2967         ADD_FAILURE() << "Should not have reached this line.";
2968     }
2969     catch (const std::invalid_argument& e)
2970     {
2971         EXPECT_STREQ(e.what(), "Element is not an object");
2972     }
2973 
2974     // Test where fails: Invalid property specified
2975     try
2976     {
2977         const json element = R"(
2978             {
2979               "register": "0x0A",
2980               "values": [ "0xCC", "0xFF" ],
2981               "masks":  [ "0x7F", "0x7F" ],
2982               "foo": 1
2983             }
2984         )"_json;
2985         parseI2CWriteBytes(element);
2986         ADD_FAILURE() << "Should not have reached this line.";
2987     }
2988     catch (const std::invalid_argument& e)
2989     {
2990         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2991     }
2992 
2993     // Test where fails: register value is invalid
2994     try
2995     {
2996         const json element = R"(
2997             {
2998               "register": "0x0Z",
2999               "values": [ "0xCC", "0xFF" ],
3000               "masks":  [ "0x7F", "0x7F" ]
3001             }
3002         )"_json;
3003         parseI2CWriteBytes(element);
3004         ADD_FAILURE() << "Should not have reached this line.";
3005     }
3006     catch (const std::invalid_argument& e)
3007     {
3008         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
3009     }
3010 
3011     // Test where fails: values value is invalid
3012     try
3013     {
3014         const json element = R"(
3015             {
3016               "register": "0x0A",
3017               "values": [ "0xCCC", "0xFF" ],
3018               "masks":  [ "0x7F", "0x7F" ]
3019             }
3020         )"_json;
3021         parseI2CWriteBytes(element);
3022         ADD_FAILURE() << "Should not have reached this line.";
3023     }
3024     catch (const std::invalid_argument& e)
3025     {
3026         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
3027     }
3028 
3029     // Test where fails: masks value is invalid
3030     try
3031     {
3032         const json element = R"(
3033             {
3034               "register": "0x0A",
3035               "values": [ "0xCC", "0xFF" ],
3036               "masks":  [ "F", "0x7F" ]
3037             }
3038         )"_json;
3039         parseI2CWriteBytes(element);
3040         ADD_FAILURE() << "Should not have reached this line.";
3041     }
3042     catch (const std::invalid_argument& e)
3043     {
3044         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
3045     }
3046 
3047     // Test where fails: number of elements in masks is invalid
3048     try
3049     {
3050         const json element = R"(
3051             {
3052               "register": "0x0A",
3053               "values": [ "0xCC", "0xFF" ],
3054               "masks":  [ "0x7F" ]
3055             }
3056         )"_json;
3057         parseI2CWriteBytes(element);
3058         ADD_FAILURE() << "Should not have reached this line.";
3059     }
3060     catch (const std::invalid_argument& e)
3061     {
3062         EXPECT_STREQ(e.what(), "Invalid number of elements in masks");
3063     }
3064 
3065     // Test where fails: Required register property not specified
3066     try
3067     {
3068         const json element = R"(
3069             {
3070               "values": [ "0xCC", "0xFF" ]
3071             }
3072         )"_json;
3073         parseI2CWriteBytes(element);
3074         ADD_FAILURE() << "Should not have reached this line.";
3075     }
3076     catch (const std::invalid_argument& e)
3077     {
3078         EXPECT_STREQ(e.what(), "Required property missing: register");
3079     }
3080 
3081     // Test where fails: Required values property not specified
3082     try
3083     {
3084         const json element = R"(
3085             {
3086               "register": "0x0A"
3087             }
3088         )"_json;
3089         parseI2CWriteBytes(element);
3090         ADD_FAILURE() << "Should not have reached this line.";
3091     }
3092     catch (const std::invalid_argument& e)
3093     {
3094         EXPECT_STREQ(e.what(), "Required property missing: values");
3095     }
3096 }
3097 
3098 TEST(ConfigFileParserTests, ParseIf)
3099 {
3100     // Test where works: Only required properties specified
3101     {
3102         const json element = R"(
3103             {
3104               "condition": { "run_rule": "is_downlevel_regulator" },
3105               "then": [ { "run_rule": "configure_downlevel_regulator" },
3106                         { "run_rule": "configure_standard_regulator" } ]
3107             }
3108         )"_json;
3109         std::unique_ptr<IfAction> action = parseIf(element);
3110         EXPECT_NE(action->getConditionAction().get(), nullptr);
3111         EXPECT_EQ(action->getThenActions().size(), 2);
3112         EXPECT_EQ(action->getElseActions().size(), 0);
3113     }
3114 
3115     // Test where works: All properties specified
3116     {
3117         const json element = R"(
3118             {
3119               "condition": { "run_rule": "is_downlevel_regulator" },
3120               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3121               "else": [ { "run_rule": "configure_standard_regulator" } ]
3122             }
3123         )"_json;
3124         std::unique_ptr<IfAction> action = parseIf(element);
3125         EXPECT_NE(action->getConditionAction().get(), nullptr);
3126         EXPECT_EQ(action->getThenActions().size(), 1);
3127         EXPECT_EQ(action->getElseActions().size(), 1);
3128     }
3129 
3130     // Test where fails: Required condition property not specified
3131     try
3132     {
3133         const json element = R"(
3134             {
3135               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3136               "else": [ { "run_rule": "configure_standard_regulator" } ]
3137             }
3138         )"_json;
3139         parseIf(element);
3140         ADD_FAILURE() << "Should not have reached this line.";
3141     }
3142     catch (const std::invalid_argument& e)
3143     {
3144         EXPECT_STREQ(e.what(), "Required property missing: condition");
3145     }
3146 
3147     // Test where fails: Required then property not specified
3148     try
3149     {
3150         const json element = R"(
3151             {
3152               "condition": { "run_rule": "is_downlevel_regulator" },
3153               "else": [ { "run_rule": "configure_standard_regulator" } ]
3154             }
3155         )"_json;
3156         parseIf(element);
3157         ADD_FAILURE() << "Should not have reached this line.";
3158     }
3159     catch (const std::invalid_argument& e)
3160     {
3161         EXPECT_STREQ(e.what(), "Required property missing: then");
3162     }
3163 
3164     // Test where fails: condition value is invalid
3165     try
3166     {
3167         const json element = R"(
3168             {
3169               "condition": 1,
3170               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3171               "else": [ { "run_rule": "configure_standard_regulator" } ]
3172             }
3173         )"_json;
3174         parseIf(element);
3175         ADD_FAILURE() << "Should not have reached this line.";
3176     }
3177     catch (const std::invalid_argument& e)
3178     {
3179         EXPECT_STREQ(e.what(), "Element is not an object");
3180     }
3181 
3182     // Test where fails: then value is invalid
3183     try
3184     {
3185         const json element = R"(
3186             {
3187               "condition": { "run_rule": "is_downlevel_regulator" },
3188               "then": "foo",
3189               "else": [ { "run_rule": "configure_standard_regulator" } ]
3190             }
3191         )"_json;
3192         parseIf(element);
3193         ADD_FAILURE() << "Should not have reached this line.";
3194     }
3195     catch (const std::invalid_argument& e)
3196     {
3197         EXPECT_STREQ(e.what(), "Element is not an array");
3198     }
3199 
3200     // Test where fails: else value is invalid
3201     try
3202     {
3203         const json element = R"(
3204             {
3205               "condition": { "run_rule": "is_downlevel_regulator" },
3206               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3207               "else": 1
3208             }
3209         )"_json;
3210         parseIf(element);
3211         ADD_FAILURE() << "Should not have reached this line.";
3212     }
3213     catch (const std::invalid_argument& e)
3214     {
3215         EXPECT_STREQ(e.what(), "Element is not an array");
3216     }
3217 
3218     // Test where fails: Invalid property specified
3219     try
3220     {
3221         const json element = R"(
3222             {
3223               "condition": { "run_rule": "is_downlevel_regulator" },
3224               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3225               "foo": "bar"
3226             }
3227         )"_json;
3228         parseIf(element);
3229         ADD_FAILURE() << "Should not have reached this line.";
3230     }
3231     catch (const std::invalid_argument& e)
3232     {
3233         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3234     }
3235 
3236     // Test where fails: Element is not an object
3237     try
3238     {
3239         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3240         parseIf(element);
3241         ADD_FAILURE() << "Should not have reached this line.";
3242     }
3243     catch (const std::invalid_argument& e)
3244     {
3245         EXPECT_STREQ(e.what(), "Element is not an object");
3246     }
3247 }
3248 
3249 TEST(ConfigFileParserTests, ParseInt8)
3250 {
3251     // Test where works: INT8_MIN
3252     {
3253         const json element = R"( -128 )"_json;
3254         int8_t value = parseInt8(element);
3255         EXPECT_EQ(value, -128);
3256     }
3257 
3258     // Test where works: INT8_MAX
3259     {
3260         const json element = R"( 127 )"_json;
3261         int8_t value = parseInt8(element);
3262         EXPECT_EQ(value, 127);
3263     }
3264 
3265     // Test where fails: Element is not an integer
3266     try
3267     {
3268         const json element = R"( 1.03 )"_json;
3269         parseInt8(element);
3270         ADD_FAILURE() << "Should not have reached this line.";
3271     }
3272     catch (const std::invalid_argument& e)
3273     {
3274         EXPECT_STREQ(e.what(), "Element is not an integer");
3275     }
3276 
3277     // Test where fails: Value < INT8_MIN
3278     try
3279     {
3280         const json element = R"( -129 )"_json;
3281         parseInt8(element);
3282         ADD_FAILURE() << "Should not have reached this line.";
3283     }
3284     catch (const std::invalid_argument& e)
3285     {
3286         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
3287     }
3288 
3289     // Test where fails: Value > INT8_MAX
3290     try
3291     {
3292         const json element = R"( 128 )"_json;
3293         parseInt8(element);
3294         ADD_FAILURE() << "Should not have reached this line.";
3295     }
3296     catch (const std::invalid_argument& e)
3297     {
3298         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
3299     }
3300 }
3301 
3302 TEST(ConfigFileParserTests, ParseInventoryPath)
3303 {
3304     // Test where works: Inventory path has a leading '/'
3305     {
3306         const json element = "/system/chassis/motherboard/cpu3";
3307         std::string value = parseInventoryPath(element);
3308         EXPECT_EQ(
3309             value,
3310             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu3");
3311     }
3312 
3313     // Test where works: Inventory path does not have a leading '/'
3314     {
3315         const json element = "system/chassis/motherboard/cpu1";
3316         std::string value = parseInventoryPath(element);
3317         EXPECT_EQ(
3318             value,
3319             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1");
3320     }
3321 
3322     // Test where fails: JSON element is not a string
3323     try
3324     {
3325         const json element = R"( { "foo": "bar" } )"_json;
3326         parseInventoryPath(element);
3327         ADD_FAILURE() << "Should not have reached this line.";
3328     }
3329     catch (const std::invalid_argument& e)
3330     {
3331         EXPECT_STREQ(e.what(), "Element is not a string");
3332     }
3333 
3334     // Test where fails: JSON element contains an empty string
3335     try
3336     {
3337         const json element = "";
3338         parseInventoryPath(element);
3339         ADD_FAILURE() << "Should not have reached this line.";
3340     }
3341     catch (const std::invalid_argument& e)
3342     {
3343         EXPECT_STREQ(e.what(), "Element contains an empty string");
3344     }
3345 }
3346 
3347 TEST(ConfigFileParserTests, ParseLogPhaseFault)
3348 {
3349     // Test where works
3350     {
3351         const json element = R"(
3352             {
3353               "type": "n+1"
3354             }
3355         )"_json;
3356         std::unique_ptr<LogPhaseFaultAction> action =
3357             parseLogPhaseFault(element);
3358         EXPECT_EQ(action->getType(), PhaseFaultType::n_plus_1);
3359     }
3360 
3361     // Test where fails: Element is not an object
3362     try
3363     {
3364         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3365         parseLogPhaseFault(element);
3366         ADD_FAILURE() << "Should not have reached this line.";
3367     }
3368     catch (const std::invalid_argument& e)
3369     {
3370         EXPECT_STREQ(e.what(), "Element is not an object");
3371     }
3372 
3373     // Test where fails: Required type property not specified
3374     try
3375     {
3376         const json element = R"(
3377             {
3378             }
3379         )"_json;
3380         parseLogPhaseFault(element);
3381         ADD_FAILURE() << "Should not have reached this line.";
3382     }
3383     catch (const std::invalid_argument& e)
3384     {
3385         EXPECT_STREQ(e.what(), "Required property missing: type");
3386     }
3387 
3388     // Test where fails: type value is invalid
3389     try
3390     {
3391         const json element = R"(
3392             {
3393               "type": "n+2"
3394             }
3395         )"_json;
3396         parseLogPhaseFault(element);
3397         ADD_FAILURE() << "Should not have reached this line.";
3398     }
3399     catch (const std::invalid_argument& e)
3400     {
3401         EXPECT_STREQ(e.what(), "Element is not a phase fault type");
3402     }
3403 
3404     // Test where fails: Invalid property specified
3405     try
3406     {
3407         const json element = R"(
3408             {
3409               "type": "n+1",
3410               "foo": 1
3411             }
3412         )"_json;
3413         parseLogPhaseFault(element);
3414         ADD_FAILURE() << "Should not have reached this line.";
3415     }
3416     catch (const std::invalid_argument& e)
3417     {
3418         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3419     }
3420 }
3421 
3422 TEST(ConfigFileParserTests, ParseNot)
3423 {
3424     // Test where works
3425     {
3426         const json element = R"(
3427             { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }
3428         )"_json;
3429         std::unique_ptr<NotAction> action = parseNot(element);
3430         EXPECT_NE(action->getAction().get(), nullptr);
3431     }
3432 
3433     // Test where fails: Element is not an object
3434     try
3435     {
3436         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3437         parseNot(element);
3438         ADD_FAILURE() << "Should not have reached this line.";
3439     }
3440     catch (const std::invalid_argument& e)
3441     {
3442         EXPECT_STREQ(e.what(), "Element is not an object");
3443     }
3444 }
3445 
3446 TEST(ConfigFileParserTests, ParseOr)
3447 {
3448     // Test where works: Element is an array with 2 actions
3449     {
3450         const json element = R"(
3451             [
3452               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
3453               { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
3454             ]
3455         )"_json;
3456         std::unique_ptr<OrAction> action = parseOr(element);
3457         EXPECT_EQ(action->getActions().size(), 2);
3458     }
3459 
3460     // Test where fails: Element is an array with 1 action
3461     try
3462     {
3463         const json element = R"(
3464             [
3465               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }
3466             ]
3467         )"_json;
3468         parseOr(element);
3469         ADD_FAILURE() << "Should not have reached this line.";
3470     }
3471     catch (const std::invalid_argument& e)
3472     {
3473         EXPECT_STREQ(e.what(), "Array must contain two or more actions");
3474     }
3475 
3476     // Test where fails: Element is not an array
3477     try
3478     {
3479         const json element = R"(
3480             {
3481               "foo": "bar"
3482             }
3483         )"_json;
3484         parseOr(element);
3485         ADD_FAILURE() << "Should not have reached this line.";
3486     }
3487     catch (const std::invalid_argument& e)
3488     {
3489         EXPECT_STREQ(e.what(), "Element is not an array");
3490     }
3491 }
3492 
3493 TEST(ConfigFileParserTests, ParsePhaseFaultDetection)
3494 {
3495     // Test where works: actions specified: optional properties not specified
3496     {
3497         const json element = R"(
3498             {
3499               "actions": [
3500                 { "run_rule": "detect_phase_fault_rule" }
3501               ]
3502             }
3503         )"_json;
3504         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
3505             parsePhaseFaultDetection(element);
3506         EXPECT_EQ(phaseFaultDetection->getActions().size(), 1);
3507         EXPECT_EQ(phaseFaultDetection->getDeviceID(), "");
3508     }
3509 
3510     // Test where works: rule_id specified: optional properties specified
3511     {
3512         const json element = R"(
3513             {
3514               "comments": [ "Detect phase fault using I/O expander" ],
3515               "device_id": "io_expander",
3516               "rule_id": "detect_phase_fault_rule"
3517             }
3518         )"_json;
3519         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
3520             parsePhaseFaultDetection(element);
3521         EXPECT_EQ(phaseFaultDetection->getActions().size(), 1);
3522         EXPECT_EQ(phaseFaultDetection->getDeviceID(), "io_expander");
3523     }
3524 
3525     // Test where fails: Element is not an object
3526     try
3527     {
3528         const json element = R"( [ "foo", "bar" ] )"_json;
3529         parsePhaseFaultDetection(element);
3530         ADD_FAILURE() << "Should not have reached this line.";
3531     }
3532     catch (const std::invalid_argument& e)
3533     {
3534         EXPECT_STREQ(e.what(), "Element is not an object");
3535     }
3536 
3537     // Test where fails: device_id value is invalid
3538     try
3539     {
3540         const json element = R"(
3541             {
3542               "device_id": 1,
3543               "rule_id": "detect_phase_fault_rule"
3544             }
3545         )"_json;
3546         parsePhaseFaultDetection(element);
3547         ADD_FAILURE() << "Should not have reached this line.";
3548     }
3549     catch (const std::invalid_argument& e)
3550     {
3551         EXPECT_STREQ(e.what(), "Element is not a string");
3552     }
3553 
3554     // Test where fails: rule_id value is invalid
3555     try
3556     {
3557         const json element = R"(
3558             {
3559               "rule_id": 1
3560             }
3561         )"_json;
3562         parsePhaseFaultDetection(element);
3563         ADD_FAILURE() << "Should not have reached this line.";
3564     }
3565     catch (const std::invalid_argument& e)
3566     {
3567         EXPECT_STREQ(e.what(), "Element is not a string");
3568     }
3569 
3570     // Test where fails: actions object is invalid
3571     try
3572     {
3573         const json element = R"(
3574             {
3575               "actions": 1
3576             }
3577         )"_json;
3578         parsePhaseFaultDetection(element);
3579         ADD_FAILURE() << "Should not have reached this line.";
3580     }
3581     catch (const std::invalid_argument& e)
3582     {
3583         EXPECT_STREQ(e.what(), "Element is not an array");
3584     }
3585 
3586     // Test where fails: Required actions or rule_id property not specified
3587     try
3588     {
3589         const json element = R"(
3590             {
3591               "device_id": "io_expander"
3592             }
3593         )"_json;
3594         parsePhaseFaultDetection(element);
3595         ADD_FAILURE() << "Should not have reached this line.";
3596     }
3597     catch (const std::invalid_argument& e)
3598     {
3599         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
3600                                "either rule_id or actions");
3601     }
3602 
3603     // Test where fails: Required actions or rule_id property both specified
3604     try
3605     {
3606         const json element = R"(
3607             {
3608               "rule_id": "detect_phase_fault_rule",
3609               "actions": [
3610                 { "run_rule": "detect_phase_fault_rule" }
3611               ]
3612             }
3613         )"_json;
3614         parsePhaseFaultDetection(element);
3615         ADD_FAILURE() << "Should not have reached this line.";
3616     }
3617     catch (const std::invalid_argument& e)
3618     {
3619         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
3620                                "either rule_id or actions");
3621     }
3622 
3623     // Test where fails: Invalid property specified
3624     try
3625     {
3626         const json element = R"(
3627             {
3628               "foo": "bar",
3629               "actions": [
3630                 { "run_rule": "detect_phase_fault_rule" }
3631               ]
3632             }
3633         )"_json;
3634         parsePhaseFaultDetection(element);
3635         ADD_FAILURE() << "Should not have reached this line.";
3636     }
3637     catch (const std::invalid_argument& e)
3638     {
3639         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3640     }
3641 }
3642 
3643 TEST(ConfigFileParserTests, ParsePhaseFaultType)
3644 {
3645     // Test where works: n
3646     {
3647         const json element = "n";
3648         PhaseFaultType type = parsePhaseFaultType(element);
3649         EXPECT_EQ(type, PhaseFaultType::n);
3650     }
3651 
3652     // Test where works: n+1
3653     {
3654         const json element = "n+1";
3655         PhaseFaultType type = parsePhaseFaultType(element);
3656         EXPECT_EQ(type, PhaseFaultType::n_plus_1);
3657     }
3658 
3659     // Test where fails: Element is not a phase fault type
3660     try
3661     {
3662         const json element = "n+2";
3663         parsePhaseFaultType(element);
3664         ADD_FAILURE() << "Should not have reached this line.";
3665     }
3666     catch (const std::invalid_argument& e)
3667     {
3668         EXPECT_STREQ(e.what(), "Element is not a phase fault type");
3669     }
3670 
3671     // Test where fails: Element is not a string
3672     try
3673     {
3674         const json element = R"( { "foo": "bar" } )"_json;
3675         parsePhaseFaultType(element);
3676         ADD_FAILURE() << "Should not have reached this line.";
3677     }
3678     catch (const std::invalid_argument& e)
3679     {
3680         EXPECT_STREQ(e.what(), "Element is not a string");
3681     }
3682 }
3683 
3684 TEST(ConfigFileParserTests, ParsePMBusReadSensor)
3685 {
3686     // Test where works: Only required properties specified
3687     {
3688         const json element = R"(
3689             {
3690               "type": "iout",
3691               "command": "0x8C",
3692               "format": "linear_11"
3693             }
3694         )"_json;
3695         std::unique_ptr<PMBusReadSensorAction> action =
3696             parsePMBusReadSensor(element);
3697         EXPECT_EQ(action->getType(), SensorType::iout);
3698         EXPECT_EQ(action->getCommand(), 0x8C);
3699         EXPECT_EQ(action->getFormat(),
3700                   pmbus_utils::SensorDataFormat::linear_11);
3701         EXPECT_EQ(action->getExponent().has_value(), false);
3702     }
3703 
3704     // Test where works: All properties specified
3705     {
3706         const json element = R"(
3707             {
3708               "type": "temperature",
3709               "command": "0x7A",
3710               "format": "linear_16",
3711               "exponent": -8
3712             }
3713         )"_json;
3714         std::unique_ptr<PMBusReadSensorAction> action =
3715             parsePMBusReadSensor(element);
3716         EXPECT_EQ(action->getType(), SensorType::temperature);
3717         EXPECT_EQ(action->getCommand(), 0x7A);
3718         EXPECT_EQ(action->getFormat(),
3719                   pmbus_utils::SensorDataFormat::linear_16);
3720         EXPECT_EQ(action->getExponent().has_value(), true);
3721         EXPECT_EQ(action->getExponent().value(), -8);
3722     }
3723 
3724     // Test where fails: Element is not an object
3725     try
3726     {
3727         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3728         parsePMBusReadSensor(element);
3729         ADD_FAILURE() << "Should not have reached this line.";
3730     }
3731     catch (const std::invalid_argument& e)
3732     {
3733         EXPECT_STREQ(e.what(), "Element is not an object");
3734     }
3735 
3736     // Test where fails: Invalid property specified
3737     try
3738     {
3739         const json element = R"(
3740             {
3741               "type": "iout",
3742               "command": "0x8C",
3743               "format": "linear_11",
3744               "foo": 1
3745             }
3746         )"_json;
3747         parsePMBusReadSensor(element);
3748         ADD_FAILURE() << "Should not have reached this line.";
3749     }
3750     catch (const std::invalid_argument& e)
3751     {
3752         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3753     }
3754 
3755     // Test where fails: Required type property not specified
3756     try
3757     {
3758         const json element = R"(
3759             {
3760               "command": "0x8C",
3761               "format": "linear_11"
3762             }
3763         )"_json;
3764         parsePMBusReadSensor(element);
3765         ADD_FAILURE() << "Should not have reached this line.";
3766     }
3767     catch (const std::invalid_argument& e)
3768     {
3769         EXPECT_STREQ(e.what(), "Required property missing: type");
3770     }
3771 
3772     // Test where fails: Required command property not specified
3773     try
3774     {
3775         const json element = R"(
3776             {
3777               "type": "iout",
3778               "format": "linear_11"
3779             }
3780         )"_json;
3781         parsePMBusReadSensor(element);
3782         ADD_FAILURE() << "Should not have reached this line.";
3783     }
3784     catch (const std::invalid_argument& e)
3785     {
3786         EXPECT_STREQ(e.what(), "Required property missing: command");
3787     }
3788 
3789     // Test where fails: Required format property not specified
3790     try
3791     {
3792         const json element = R"(
3793             {
3794               "type": "iout",
3795               "command": "0x8C"
3796             }
3797         )"_json;
3798         parsePMBusReadSensor(element);
3799         ADD_FAILURE() << "Should not have reached this line.";
3800     }
3801     catch (const std::invalid_argument& e)
3802     {
3803         EXPECT_STREQ(e.what(), "Required property missing: format");
3804     }
3805 
3806     // Test where fails: type value is invalid
3807     try
3808     {
3809         const json element = R"(
3810             {
3811               "type": 1,
3812               "command": "0x7A",
3813               "format": "linear_16"
3814             }
3815         )"_json;
3816         parsePMBusReadSensor(element);
3817         ADD_FAILURE() << "Should not have reached this line.";
3818     }
3819     catch (const std::invalid_argument& e)
3820     {
3821         EXPECT_STREQ(e.what(), "Element is not a string");
3822     }
3823 
3824     // Test where fails: command value is invalid
3825     try
3826     {
3827         const json element = R"(
3828             {
3829               "type": "temperature",
3830               "command": 0,
3831               "format": "linear_16"
3832             }
3833         )"_json;
3834         parsePMBusReadSensor(element);
3835         ADD_FAILURE() << "Should not have reached this line.";
3836     }
3837     catch (const std::invalid_argument& e)
3838     {
3839         EXPECT_STREQ(e.what(), "Element is not a string");
3840     }
3841 
3842     // Test where fails: format value is invalid
3843     try
3844     {
3845         const json element = R"(
3846             {
3847               "type": "temperature",
3848               "command": "0x7A",
3849               "format": 1
3850             }
3851         )"_json;
3852         parsePMBusReadSensor(element);
3853         ADD_FAILURE() << "Should not have reached this line.";
3854     }
3855     catch (const std::invalid_argument& e)
3856     {
3857         EXPECT_STREQ(e.what(), "Element is not a string");
3858     }
3859 
3860     // Test where fails: exponent value is invalid
3861     try
3862     {
3863         const json element = R"(
3864             {
3865               "type": "temperature",
3866               "command": "0x7A",
3867               "format": "linear_16",
3868               "exponent": 1.3
3869             }
3870         )"_json;
3871         parsePMBusReadSensor(element);
3872         ADD_FAILURE() << "Should not have reached this line.";
3873     }
3874     catch (const std::invalid_argument& e)
3875     {
3876         EXPECT_STREQ(e.what(), "Element is not an integer");
3877     }
3878 }
3879 
3880 TEST(ConfigFileParserTests, ParsePMBusWriteVoutCommand)
3881 {
3882     // Test where works: Only required properties specified
3883     {
3884         const json element = R"(
3885             {
3886               "format": "linear"
3887             }
3888         )"_json;
3889         std::unique_ptr<PMBusWriteVoutCommandAction> action =
3890             parsePMBusWriteVoutCommand(element);
3891         EXPECT_EQ(action->getVolts().has_value(), false);
3892         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
3893         EXPECT_EQ(action->getExponent().has_value(), false);
3894         EXPECT_EQ(action->isVerified(), false);
3895     }
3896 
3897     // Test where works: All properties specified
3898     {
3899         const json element = R"(
3900             {
3901               "volts": 1.03,
3902               "format": "linear",
3903               "exponent": -8,
3904               "is_verified": true
3905             }
3906         )"_json;
3907         std::unique_ptr<PMBusWriteVoutCommandAction> action =
3908             parsePMBusWriteVoutCommand(element);
3909         EXPECT_EQ(action->getVolts().has_value(), true);
3910         EXPECT_EQ(action->getVolts().value(), 1.03);
3911         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
3912         EXPECT_EQ(action->getExponent().has_value(), true);
3913         EXPECT_EQ(action->getExponent().value(), -8);
3914         EXPECT_EQ(action->isVerified(), true);
3915     }
3916 
3917     // Test where fails: Element is not an object
3918     try
3919     {
3920         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3921         parsePMBusWriteVoutCommand(element);
3922         ADD_FAILURE() << "Should not have reached this line.";
3923     }
3924     catch (const std::invalid_argument& e)
3925     {
3926         EXPECT_STREQ(e.what(), "Element is not an object");
3927     }
3928 
3929     // Test where fails: volts value is invalid
3930     try
3931     {
3932         const json element = R"(
3933             {
3934               "volts": "foo",
3935               "format": "linear"
3936             }
3937         )"_json;
3938         parsePMBusWriteVoutCommand(element);
3939         ADD_FAILURE() << "Should not have reached this line.";
3940     }
3941     catch (const std::invalid_argument& e)
3942     {
3943         EXPECT_STREQ(e.what(), "Element is not a number");
3944     }
3945 
3946     // Test where fails: Required format property not specified
3947     try
3948     {
3949         const json element = R"(
3950             {
3951               "volts": 1.03,
3952               "is_verified": true
3953             }
3954         )"_json;
3955         parsePMBusWriteVoutCommand(element);
3956         ADD_FAILURE() << "Should not have reached this line.";
3957     }
3958     catch (const std::invalid_argument& e)
3959     {
3960         EXPECT_STREQ(e.what(), "Required property missing: format");
3961     }
3962 
3963     // Test where fails: format value is invalid
3964     try
3965     {
3966         const json element = R"(
3967             {
3968               "format": "linear_11"
3969             }
3970         )"_json;
3971         parsePMBusWriteVoutCommand(element);
3972         ADD_FAILURE() << "Should not have reached this line.";
3973     }
3974     catch (const std::invalid_argument& e)
3975     {
3976         EXPECT_STREQ(e.what(), "Invalid format value: linear_11");
3977     }
3978 
3979     // Test where fails: exponent value is invalid
3980     try
3981     {
3982         const json element = R"(
3983             {
3984               "format": "linear",
3985               "exponent": 1.3
3986             }
3987         )"_json;
3988         parsePMBusWriteVoutCommand(element);
3989         ADD_FAILURE() << "Should not have reached this line.";
3990     }
3991     catch (const std::invalid_argument& e)
3992     {
3993         EXPECT_STREQ(e.what(), "Element is not an integer");
3994     }
3995 
3996     // Test where fails: is_verified value is invalid
3997     try
3998     {
3999         const json element = R"(
4000             {
4001               "format": "linear",
4002               "is_verified": "true"
4003             }
4004         )"_json;
4005         parsePMBusWriteVoutCommand(element);
4006         ADD_FAILURE() << "Should not have reached this line.";
4007     }
4008     catch (const std::invalid_argument& e)
4009     {
4010         EXPECT_STREQ(e.what(), "Element is not a boolean");
4011     }
4012 
4013     // Test where fails: Invalid property specified
4014     try
4015     {
4016         const json element = R"(
4017             {
4018               "format": "linear",
4019               "foo": "bar"
4020             }
4021         )"_json;
4022         parsePMBusWriteVoutCommand(element);
4023         ADD_FAILURE() << "Should not have reached this line.";
4024     }
4025     catch (const std::invalid_argument& e)
4026     {
4027         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4028     }
4029 }
4030 
4031 TEST(ConfigFileParserTests, ParsePresenceDetection)
4032 {
4033     // Test where works: actions property specified
4034     {
4035         const json element = R"(
4036             {
4037               "actions": [
4038                 { "run_rule": "read_sensors_rule" }
4039               ]
4040             }
4041         )"_json;
4042         std::unique_ptr<PresenceDetection> presenceDetection =
4043             parsePresenceDetection(element);
4044         EXPECT_EQ(presenceDetection->getActions().size(), 1);
4045     }
4046 
4047     // Test where works: rule_id property specified
4048     {
4049         const json element = R"(
4050             {
4051               "comments": [ "comments property" ],
4052               "rule_id": "set_voltage_rule"
4053             }
4054         )"_json;
4055         std::unique_ptr<PresenceDetection> presenceDetection =
4056             parsePresenceDetection(element);
4057         EXPECT_EQ(presenceDetection->getActions().size(), 1);
4058     }
4059 
4060     // Test where fails: actions object is invalid
4061     try
4062     {
4063         const json element = R"(
4064             {
4065               "actions": 1
4066             }
4067         )"_json;
4068         parsePresenceDetection(element);
4069         ADD_FAILURE() << "Should not have reached this line.";
4070     }
4071     catch (const std::invalid_argument& e)
4072     {
4073         EXPECT_STREQ(e.what(), "Element is not an array");
4074     }
4075 
4076     // Test where fails: rule_id value is invalid
4077     try
4078     {
4079         const json element = R"(
4080             {
4081               "rule_id": 1
4082             }
4083         )"_json;
4084         parsePresenceDetection(element);
4085         ADD_FAILURE() << "Should not have reached this line.";
4086     }
4087     catch (const std::invalid_argument& e)
4088     {
4089         EXPECT_STREQ(e.what(), "Element is not a string");
4090     }
4091 
4092     // Test where fails: Required actions or rule_id property not specified
4093     try
4094     {
4095         const json element = R"(
4096             {
4097               "comments": [ "comments property" ]
4098             }
4099         )"_json;
4100         parsePresenceDetection(element);
4101         ADD_FAILURE() << "Should not have reached this line.";
4102     }
4103     catch (const std::invalid_argument& e)
4104     {
4105         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4106                                "either rule_id or actions");
4107     }
4108 
4109     // Test where fails: Required actions or rule_id property both specified
4110     try
4111     {
4112         const json element = R"(
4113             {
4114               "rule_id": "set_voltage_rule",
4115               "actions": [
4116                 { "run_rule": "read_sensors_rule" }
4117               ]
4118             }
4119         )"_json;
4120         parsePresenceDetection(element);
4121         ADD_FAILURE() << "Should not have reached this line.";
4122     }
4123     catch (const std::invalid_argument& e)
4124     {
4125         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4126                                "either rule_id or actions");
4127     }
4128 
4129     // Test where fails: Element is not an object
4130     try
4131     {
4132         const json element = R"( [ "foo", "bar" ] )"_json;
4133         parsePresenceDetection(element);
4134         ADD_FAILURE() << "Should not have reached this line.";
4135     }
4136     catch (const std::invalid_argument& e)
4137     {
4138         EXPECT_STREQ(e.what(), "Element is not an object");
4139     }
4140 
4141     // Test where fails: Invalid property specified
4142     try
4143     {
4144         const json element = R"(
4145             {
4146               "foo": "bar",
4147               "actions": [
4148                 { "run_rule": "read_sensors_rule" }
4149               ]
4150             }
4151         )"_json;
4152         parsePresenceDetection(element);
4153         ADD_FAILURE() << "Should not have reached this line.";
4154     }
4155     catch (const std::invalid_argument& e)
4156     {
4157         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4158     }
4159 }
4160 
4161 TEST(ConfigFileParserTests, ParseRail)
4162 {
4163     // Test where works: Only required properties specified
4164     {
4165         const json element = R"(
4166             {
4167               "id": "vdd"
4168             }
4169         )"_json;
4170         std::unique_ptr<Rail> rail = parseRail(element);
4171         EXPECT_EQ(rail->getID(), "vdd");
4172         EXPECT_EQ(rail->getConfiguration(), nullptr);
4173         EXPECT_EQ(rail->getSensorMonitoring(), nullptr);
4174     }
4175 
4176     // Test where works: All properties specified
4177     {
4178         const json element = R"(
4179             {
4180               "comments": [ "comments property" ],
4181               "id": "vdd",
4182               "configuration": {
4183                 "volts": 1.1,
4184                 "actions": [
4185                   {
4186                     "pmbus_write_vout_command": {
4187                       "format": "linear"
4188                     }
4189                   }
4190                 ]
4191               },
4192               "sensor_monitoring": {
4193                 "actions": [
4194                   { "run_rule": "read_sensors_rule" }
4195                 ]
4196               }
4197             }
4198         )"_json;
4199         std::unique_ptr<Rail> rail = parseRail(element);
4200         EXPECT_EQ(rail->getID(), "vdd");
4201         EXPECT_NE(rail->getConfiguration(), nullptr);
4202         EXPECT_NE(rail->getSensorMonitoring(), nullptr);
4203     }
4204 
4205     // Test where fails: id property not specified
4206     try
4207     {
4208         const json element = R"(
4209             {
4210               "configuration": {
4211                 "volts": 1.1,
4212                 "actions": [
4213                   {
4214                     "pmbus_write_vout_command": {
4215                       "format": "linear"
4216                     }
4217                   }
4218                 ]
4219               }
4220             }
4221         )"_json;
4222         parseRail(element);
4223         ADD_FAILURE() << "Should not have reached this line.";
4224     }
4225     catch (const std::invalid_argument& e)
4226     {
4227         EXPECT_STREQ(e.what(), "Required property missing: id");
4228     }
4229 
4230     // Test where fails: id property is invalid
4231     try
4232     {
4233         const json element = R"(
4234             {
4235               "id": "",
4236               "configuration": {
4237                 "volts": 1.1,
4238                 "actions": [
4239                   {
4240                     "pmbus_write_vout_command": {
4241                       "format": "linear"
4242                     }
4243                   }
4244                 ]
4245               }
4246             }
4247         )"_json;
4248         parseRail(element);
4249         ADD_FAILURE() << "Should not have reached this line.";
4250     }
4251     catch (const std::invalid_argument& e)
4252     {
4253         EXPECT_STREQ(e.what(), "Element contains an empty string");
4254     }
4255 
4256     // Test where fails: Element is not an object
4257     try
4258     {
4259         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4260         parseRail(element);
4261         ADD_FAILURE() << "Should not have reached this line.";
4262     }
4263     catch (const std::invalid_argument& e)
4264     {
4265         EXPECT_STREQ(e.what(), "Element is not an object");
4266     }
4267 
4268     // Test where fails: configuration value is invalid
4269     try
4270     {
4271         const json element = R"(
4272             {
4273               "id": "vdd",
4274               "configuration": "config"
4275             }
4276         )"_json;
4277         parseRail(element);
4278         ADD_FAILURE() << "Should not have reached this line.";
4279     }
4280     catch (const std::invalid_argument& e)
4281     {
4282         EXPECT_STREQ(e.what(), "Element is not an object");
4283     }
4284 
4285     // Test where fails: sensor_monitoring value is invalid
4286     try
4287     {
4288         const json element = R"(
4289             {
4290               "comments": [ "comments property" ],
4291               "id": "vdd",
4292               "configuration": {
4293                 "volts": 1.1,
4294                 "actions": [
4295                   {
4296                     "pmbus_write_vout_command": {
4297                       "format": "linear"
4298                     }
4299                   }
4300                 ]
4301               },
4302               "sensor_monitoring": 1
4303             }
4304         )"_json;
4305         parseRail(element);
4306         ADD_FAILURE() << "Should not have reached this line.";
4307     }
4308     catch (const std::invalid_argument& e)
4309     {
4310         EXPECT_STREQ(e.what(), "Element is not an object");
4311     }
4312 
4313     // Test where fails: Invalid property specified
4314     try
4315     {
4316         const json element = R"(
4317             {
4318               "id": "vdd",
4319               "foo" : true
4320             }
4321         )"_json;
4322         parseRail(element);
4323         ADD_FAILURE() << "Should not have reached this line.";
4324     }
4325     catch (const std::invalid_argument& e)
4326     {
4327         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4328     }
4329 }
4330 
4331 TEST(ConfigFileParserTests, ParseRailArray)
4332 {
4333     // Test where works
4334     {
4335         const json element = R"(
4336             [
4337               { "id": "vdd" },
4338               { "id": "vio" }
4339             ]
4340         )"_json;
4341         std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
4342         EXPECT_EQ(rails.size(), 2);
4343         EXPECT_EQ(rails[0]->getID(), "vdd");
4344         EXPECT_EQ(rails[1]->getID(), "vio");
4345     }
4346 
4347     // Test where fails: Element is not an array
4348     try
4349     {
4350         const json element = R"(
4351             {
4352               "foo": "bar"
4353             }
4354         )"_json;
4355         parseRailArray(element);
4356         ADD_FAILURE() << "Should not have reached this line.";
4357     }
4358     catch (const std::invalid_argument& e)
4359     {
4360         EXPECT_STREQ(e.what(), "Element is not an array");
4361     }
4362 }
4363 
4364 TEST(ConfigFileParserTests, ParseRoot)
4365 {
4366     // Test where works: Only required properties specified
4367     {
4368         const json element = R"(
4369             {
4370               "chassis": [
4371                 { "number": 1 }
4372               ]
4373             }
4374         )"_json;
4375         std::vector<std::unique_ptr<Rule>> rules{};
4376         std::vector<std::unique_ptr<Chassis>> chassis{};
4377         std::tie(rules, chassis) = parseRoot(element);
4378         EXPECT_EQ(rules.size(), 0);
4379         EXPECT_EQ(chassis.size(), 1);
4380     }
4381 
4382     // Test where works: All properties specified
4383     {
4384         const json element = R"(
4385             {
4386               "comments": [ "Config file for a FooBar one-chassis system" ],
4387               "rules": [
4388                 {
4389                   "id": "set_voltage_rule",
4390                   "actions": [
4391                     { "pmbus_write_vout_command": { "format": "linear" } }
4392                   ]
4393                 }
4394               ],
4395               "chassis": [
4396                 { "number": 1 },
4397                 { "number": 3 }
4398               ]
4399             }
4400         )"_json;
4401         std::vector<std::unique_ptr<Rule>> rules{};
4402         std::vector<std::unique_ptr<Chassis>> chassis{};
4403         std::tie(rules, chassis) = parseRoot(element);
4404         EXPECT_EQ(rules.size(), 1);
4405         EXPECT_EQ(chassis.size(), 2);
4406     }
4407 
4408     // Test where fails: Element is not an object
4409     try
4410     {
4411         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4412         parseRoot(element);
4413         ADD_FAILURE() << "Should not have reached this line.";
4414     }
4415     catch (const std::invalid_argument& e)
4416     {
4417         EXPECT_STREQ(e.what(), "Element is not an object");
4418     }
4419 
4420     // Test where fails: chassis property not specified
4421     try
4422     {
4423         const json element = R"(
4424             {
4425               "rules": [
4426                 {
4427                   "id": "set_voltage_rule",
4428                   "actions": [
4429                     { "pmbus_write_vout_command": { "format": "linear" } }
4430                   ]
4431                 }
4432               ]
4433             }
4434         )"_json;
4435         parseRoot(element);
4436         ADD_FAILURE() << "Should not have reached this line.";
4437     }
4438     catch (const std::invalid_argument& e)
4439     {
4440         EXPECT_STREQ(e.what(), "Required property missing: chassis");
4441     }
4442 
4443     // Test where fails: Invalid property specified
4444     try
4445     {
4446         const json element = R"(
4447             {
4448               "remarks": [ "Config file for a FooBar one-chassis system" ],
4449               "chassis": [
4450                 { "number": 1 }
4451               ]
4452             }
4453         )"_json;
4454         parseRoot(element);
4455         ADD_FAILURE() << "Should not have reached this line.";
4456     }
4457     catch (const std::invalid_argument& e)
4458     {
4459         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4460     }
4461 }
4462 
4463 TEST(ConfigFileParserTests, ParseRule)
4464 {
4465     // Test where works: comments property specified
4466     {
4467         const json element = R"(
4468             {
4469               "comments": [ "Set voltage rule" ],
4470               "id": "set_voltage_rule",
4471               "actions": [
4472                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4473                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
4474               ]
4475             }
4476         )"_json;
4477         std::unique_ptr<Rule> rule = parseRule(element);
4478         EXPECT_EQ(rule->getID(), "set_voltage_rule");
4479         EXPECT_EQ(rule->getActions().size(), 2);
4480     }
4481 
4482     // Test where works: comments property not specified
4483     {
4484         const json element = R"(
4485             {
4486               "id": "set_voltage_rule",
4487               "actions": [
4488                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4489                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } },
4490                 { "pmbus_write_vout_command": { "volts": 1.05, "format": "linear" } }
4491               ]
4492             }
4493         )"_json;
4494         std::unique_ptr<Rule> rule = parseRule(element);
4495         EXPECT_EQ(rule->getID(), "set_voltage_rule");
4496         EXPECT_EQ(rule->getActions().size(), 3);
4497     }
4498 
4499     // Test where fails: Element is not an object
4500     try
4501     {
4502         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4503         parseRule(element);
4504         ADD_FAILURE() << "Should not have reached this line.";
4505     }
4506     catch (const std::invalid_argument& e)
4507     {
4508         EXPECT_STREQ(e.what(), "Element is not an object");
4509     }
4510 
4511     // Test where fails: id property not specified
4512     try
4513     {
4514         const json element = R"(
4515             {
4516               "actions": [
4517                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4518               ]
4519             }
4520         )"_json;
4521         parseRule(element);
4522         ADD_FAILURE() << "Should not have reached this line.";
4523     }
4524     catch (const std::invalid_argument& e)
4525     {
4526         EXPECT_STREQ(e.what(), "Required property missing: id");
4527     }
4528 
4529     // Test where fails: id property is invalid
4530     try
4531     {
4532         const json element = R"(
4533             {
4534               "id": "",
4535               "actions": [
4536                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4537               ]
4538             }
4539         )"_json;
4540         parseRule(element);
4541         ADD_FAILURE() << "Should not have reached this line.";
4542     }
4543     catch (const std::invalid_argument& e)
4544     {
4545         EXPECT_STREQ(e.what(), "Element contains an empty string");
4546     }
4547 
4548     // Test where fails: actions property not specified
4549     try
4550     {
4551         const json element = R"(
4552             {
4553               "comments": [ "Set voltage rule" ],
4554               "id": "set_voltage_rule"
4555             }
4556         )"_json;
4557         parseRule(element);
4558         ADD_FAILURE() << "Should not have reached this line.";
4559     }
4560     catch (const std::invalid_argument& e)
4561     {
4562         EXPECT_STREQ(e.what(), "Required property missing: actions");
4563     }
4564 
4565     // Test where fails: actions property is invalid
4566     try
4567     {
4568         const json element = R"(
4569             {
4570               "id": "set_voltage_rule",
4571               "actions": true
4572             }
4573         )"_json;
4574         parseRule(element);
4575         ADD_FAILURE() << "Should not have reached this line.";
4576     }
4577     catch (const std::invalid_argument& e)
4578     {
4579         EXPECT_STREQ(e.what(), "Element is not an array");
4580     }
4581 
4582     // Test where fails: Invalid property specified
4583     try
4584     {
4585         const json element = R"(
4586             {
4587               "remarks": [ "Set voltage rule" ],
4588               "id": "set_voltage_rule",
4589               "actions": [
4590                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4591               ]
4592             }
4593         )"_json;
4594         parseRule(element);
4595         ADD_FAILURE() << "Should not have reached this line.";
4596     }
4597     catch (const std::invalid_argument& e)
4598     {
4599         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4600     }
4601 }
4602 
4603 TEST(ConfigFileParserTests, ParseRuleArray)
4604 {
4605     // Test where works
4606     {
4607         const json element = R"(
4608             [
4609               {
4610                 "id": "set_voltage_rule1",
4611                 "actions": [
4612                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4613                 ]
4614               },
4615               {
4616                 "id": "set_voltage_rule2",
4617                 "actions": [
4618                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4619                   { "pmbus_write_vout_command": { "volts": 1.11, "format": "linear" } }
4620                 ]
4621               }
4622             ]
4623         )"_json;
4624         std::vector<std::unique_ptr<Rule>> rules = parseRuleArray(element);
4625         EXPECT_EQ(rules.size(), 2);
4626         EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
4627         EXPECT_EQ(rules[0]->getActions().size(), 1);
4628         EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
4629         EXPECT_EQ(rules[1]->getActions().size(), 2);
4630     }
4631 
4632     // Test where fails: Element is not an array
4633     try
4634     {
4635         const json element = R"( { "id": "set_voltage_rule" } )"_json;
4636         parseRuleArray(element);
4637         ADD_FAILURE() << "Should not have reached this line.";
4638     }
4639     catch (const std::invalid_argument& e)
4640     {
4641         EXPECT_STREQ(e.what(), "Element is not an array");
4642     }
4643 }
4644 
4645 TEST(ConfigFileParserTests, ParseRuleIDOrActionsProperty)
4646 {
4647     // Test where works: actions specified
4648     {
4649         const json element = R"(
4650             {
4651               "actions": [
4652                 { "pmbus_write_vout_command": { "format": "linear" } },
4653                 { "run_rule": "set_voltage_rule" }
4654               ]
4655             }
4656         )"_json;
4657         std::vector<std::unique_ptr<Action>> actions =
4658             parseRuleIDOrActionsProperty(element);
4659         EXPECT_EQ(actions.size(), 2);
4660     }
4661 
4662     // Test where works: rule_id specified
4663     {
4664         const json element = R"(
4665             {
4666               "rule_id": "set_voltage_rule"
4667             }
4668         )"_json;
4669         std::vector<std::unique_ptr<Action>> actions =
4670             parseRuleIDOrActionsProperty(element);
4671         EXPECT_EQ(actions.size(), 1);
4672     }
4673 
4674     // Test where fails: Element is not an object
4675     try
4676     {
4677         const json element = R"( [ "foo", "bar" ] )"_json;
4678         parseRuleIDOrActionsProperty(element);
4679         ADD_FAILURE() << "Should not have reached this line.";
4680     }
4681     catch (const std::invalid_argument& e)
4682     {
4683         EXPECT_STREQ(e.what(), "Element is not an object");
4684     }
4685 
4686     // Test where fails: rule_id is invalid
4687     try
4688     {
4689         const json element = R"(
4690             { "rule_id": 1 }
4691         )"_json;
4692         parseRuleIDOrActionsProperty(element);
4693         ADD_FAILURE() << "Should not have reached this line.";
4694     }
4695     catch (const std::invalid_argument& e)
4696     {
4697         EXPECT_STREQ(e.what(), "Element is not a string");
4698     }
4699 
4700     // Test where fails: actions is invalid
4701     try
4702     {
4703         const json element = R"(
4704             { "actions": 1 }
4705         )"_json;
4706         parseRuleIDOrActionsProperty(element);
4707         ADD_FAILURE() << "Should not have reached this line.";
4708     }
4709     catch (const std::invalid_argument& e)
4710     {
4711         EXPECT_STREQ(e.what(), "Element is not an array");
4712     }
4713 
4714     // Test where fails: Neither rule_id nor actions specified
4715     try
4716     {
4717         const json element = R"(
4718             {
4719               "volts": 1.03
4720             }
4721         )"_json;
4722         parseRuleIDOrActionsProperty(element);
4723         ADD_FAILURE() << "Should not have reached this line.";
4724     }
4725     catch (const std::invalid_argument& e)
4726     {
4727         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4728                                "either rule_id or actions");
4729     }
4730 
4731     // Test where fails: Both rule_id and actions specified
4732     try
4733     {
4734         const json element = R"(
4735             {
4736               "volts": 1.03,
4737               "rule_id": "set_voltage_rule",
4738               "actions": [
4739                 {
4740                   "pmbus_write_vout_command": {
4741                     "format": "linear"
4742                   }
4743                 }
4744               ]
4745             }
4746         )"_json;
4747         parseRuleIDOrActionsProperty(element);
4748         ADD_FAILURE() << "Should not have reached this line.";
4749     }
4750     catch (const std::invalid_argument& e)
4751     {
4752         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4753                                "either rule_id or actions");
4754     }
4755 }
4756 
4757 TEST(ConfigFileParserTests, ParseRunRule)
4758 {
4759     // Test where works
4760     {
4761         const json element = "vdd_regulator";
4762         std::unique_ptr<RunRuleAction> action = parseRunRule(element);
4763         EXPECT_EQ(action->getRuleID(), "vdd_regulator");
4764     }
4765 
4766     // Test where fails: Element is not a string
4767     try
4768     {
4769         const json element = 1;
4770         parseRunRule(element);
4771         ADD_FAILURE() << "Should not have reached this line.";
4772     }
4773     catch (const std::invalid_argument& e)
4774     {
4775         EXPECT_STREQ(e.what(), "Element is not a string");
4776     }
4777 
4778     // Test where fails: Empty string
4779     try
4780     {
4781         const json element = "";
4782         parseRunRule(element);
4783         ADD_FAILURE() << "Should not have reached this line.";
4784     }
4785     catch (const std::invalid_argument& e)
4786     {
4787         EXPECT_STREQ(e.what(), "Element contains an empty string");
4788     }
4789 }
4790 
4791 TEST(ConfigFileParserTests, ParseSensorDataFormat)
4792 {
4793     // Test where works: linear_11
4794     {
4795         const json element = "linear_11";
4796         pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element);
4797         pmbus_utils::SensorDataFormat format =
4798             pmbus_utils::SensorDataFormat::linear_11;
4799         EXPECT_EQ(value, format);
4800     }
4801 
4802     // Test where works: linear_16
4803     {
4804         const json element = "linear_16";
4805         pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element);
4806         pmbus_utils::SensorDataFormat format =
4807             pmbus_utils::SensorDataFormat::linear_16;
4808         EXPECT_EQ(value, format);
4809     }
4810 
4811     // Test where fails: Element is not a sensor data format
4812     try
4813     {
4814         const json element = "foo";
4815         parseSensorDataFormat(element);
4816         ADD_FAILURE() << "Should not have reached this line.";
4817     }
4818     catch (const std::invalid_argument& e)
4819     {
4820         EXPECT_STREQ(e.what(), "Element is not a sensor data format");
4821     }
4822 
4823     // Test where fails: Element is not a string
4824     try
4825     {
4826         const json element = R"( { "foo": "bar" } )"_json;
4827         parseSensorDataFormat(element);
4828         ADD_FAILURE() << "Should not have reached this line.";
4829     }
4830     catch (const std::invalid_argument& e)
4831     {
4832         EXPECT_STREQ(e.what(), "Element is not a string");
4833     }
4834 }
4835 
4836 TEST(ConfigFileParserTests, ParseSensorMonitoring)
4837 {
4838     // Test where works: actions property specified
4839     {
4840         const json element = R"(
4841             {
4842               "actions": [
4843                 { "run_rule": "read_sensors_rule" }
4844               ]
4845             }
4846         )"_json;
4847         std::unique_ptr<SensorMonitoring> sensorMonitoring =
4848             parseSensorMonitoring(element);
4849         EXPECT_EQ(sensorMonitoring->getActions().size(), 1);
4850     }
4851 
4852     // Test where works: rule_id property specified
4853     {
4854         const json element = R"(
4855             {
4856               "comments": [ "comments property" ],
4857               "rule_id": "set_voltage_rule"
4858             }
4859         )"_json;
4860         std::unique_ptr<SensorMonitoring> sensorMonitoring =
4861             parseSensorMonitoring(element);
4862         EXPECT_EQ(sensorMonitoring->getActions().size(), 1);
4863     }
4864 
4865     // Test where fails: actions object is invalid
4866     try
4867     {
4868         const json element = R"(
4869             {
4870               "actions": 1
4871             }
4872         )"_json;
4873         parseSensorMonitoring(element);
4874         ADD_FAILURE() << "Should not have reached this line.";
4875     }
4876     catch (const std::invalid_argument& e)
4877     {
4878         EXPECT_STREQ(e.what(), "Element is not an array");
4879     }
4880 
4881     // Test where fails: rule_id value is invalid
4882     try
4883     {
4884         const json element = R"(
4885             {
4886               "rule_id": 1
4887             }
4888         )"_json;
4889         parseSensorMonitoring(element);
4890         ADD_FAILURE() << "Should not have reached this line.";
4891     }
4892     catch (const std::invalid_argument& e)
4893     {
4894         EXPECT_STREQ(e.what(), "Element is not a string");
4895     }
4896 
4897     // Test where fails: Required actions or rule_id property not specified
4898     try
4899     {
4900         const json element = R"(
4901             {
4902               "comments": [ "comments property" ]
4903             }
4904         )"_json;
4905         parseSensorMonitoring(element);
4906         ADD_FAILURE() << "Should not have reached this line.";
4907     }
4908     catch (const std::invalid_argument& e)
4909     {
4910         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4911                                "either rule_id or actions");
4912     }
4913 
4914     // Test where fails: Required actions or rule_id property both specified
4915     try
4916     {
4917         const json element = R"(
4918             {
4919               "rule_id": "set_voltage_rule",
4920               "actions": [
4921                 { "run_rule": "read_sensors_rule" }
4922               ]
4923             }
4924         )"_json;
4925         parseSensorMonitoring(element);
4926         ADD_FAILURE() << "Should not have reached this line.";
4927     }
4928     catch (const std::invalid_argument& e)
4929     {
4930         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4931                                "either rule_id or actions");
4932     }
4933 
4934     // Test where fails: Element is not an object
4935     try
4936     {
4937         const json element = R"( [ "foo", "bar" ] )"_json;
4938         parseSensorMonitoring(element);
4939         ADD_FAILURE() << "Should not have reached this line.";
4940     }
4941     catch (const std::invalid_argument& e)
4942     {
4943         EXPECT_STREQ(e.what(), "Element is not an object");
4944     }
4945 
4946     // Test where fails: Invalid property specified
4947     try
4948     {
4949         const json element = R"(
4950             {
4951               "foo": "bar",
4952               "actions": [
4953                 { "run_rule": "read_sensors_rule" }
4954               ]
4955             }
4956         )"_json;
4957         parseSensorMonitoring(element);
4958         ADD_FAILURE() << "Should not have reached this line.";
4959     }
4960     catch (const std::invalid_argument& e)
4961     {
4962         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4963     }
4964 }
4965 
4966 TEST(ConfigFileParserTests, ParseSensorType)
4967 {
4968     // Test where works: iout
4969     {
4970         const json element = "iout";
4971         SensorType type = parseSensorType(element);
4972         EXPECT_EQ(type, SensorType::iout);
4973     }
4974 
4975     // Test where works: iout_peak
4976     {
4977         const json element = "iout_peak";
4978         SensorType type = parseSensorType(element);
4979         EXPECT_EQ(type, SensorType::iout_peak);
4980     }
4981 
4982     // Test where works: iout_valley
4983     {
4984         const json element = "iout_valley";
4985         SensorType type = parseSensorType(element);
4986         EXPECT_EQ(type, SensorType::iout_valley);
4987     }
4988 
4989     // Test where works: pout
4990     {
4991         const json element = "pout";
4992         SensorType type = parseSensorType(element);
4993         EXPECT_EQ(type, SensorType::pout);
4994     }
4995 
4996     // Test where works: temperature
4997     {
4998         const json element = "temperature";
4999         SensorType type = parseSensorType(element);
5000         EXPECT_EQ(type, SensorType::temperature);
5001     }
5002 
5003     // Test where works: temperature_peak
5004     {
5005         const json element = "temperature_peak";
5006         SensorType type = parseSensorType(element);
5007         EXPECT_EQ(type, SensorType::temperature_peak);
5008     }
5009 
5010     // Test where works: vout
5011     {
5012         const json element = "vout";
5013         SensorType type = parseSensorType(element);
5014         EXPECT_EQ(type, SensorType::vout);
5015     }
5016 
5017     // Test where works: vout_peak
5018     {
5019         const json element = "vout_peak";
5020         SensorType type = parseSensorType(element);
5021         EXPECT_EQ(type, SensorType::vout_peak);
5022     }
5023 
5024     // Test where works: vout_valley
5025     {
5026         const json element = "vout_valley";
5027         SensorType type = parseSensorType(element);
5028         EXPECT_EQ(type, SensorType::vout_valley);
5029     }
5030 
5031     // Test where fails: Element is not a sensor type
5032     try
5033     {
5034         const json element = "foo";
5035         parseSensorType(element);
5036         ADD_FAILURE() << "Should not have reached this line.";
5037     }
5038     catch (const std::invalid_argument& e)
5039     {
5040         EXPECT_STREQ(e.what(), "Element is not a sensor type");
5041     }
5042 
5043     // Test where fails: Element is not a string
5044     try
5045     {
5046         const json element = R"( { "foo": "bar" } )"_json;
5047         parseSensorType(element);
5048         ADD_FAILURE() << "Should not have reached this line.";
5049     }
5050     catch (const std::invalid_argument& e)
5051     {
5052         EXPECT_STREQ(e.what(), "Element is not a string");
5053     }
5054 }
5055 
5056 TEST(ConfigFileParserTests, ParseSetDevice)
5057 {
5058     // Test where works
5059     {
5060         const json element = "regulator1";
5061         std::unique_ptr<SetDeviceAction> action = parseSetDevice(element);
5062         EXPECT_EQ(action->getDeviceID(), "regulator1");
5063     }
5064 
5065     // Test where fails: Element is not a string
5066     try
5067     {
5068         const json element = 1;
5069         parseSetDevice(element);
5070         ADD_FAILURE() << "Should not have reached this line.";
5071     }
5072     catch (const std::invalid_argument& e)
5073     {
5074         EXPECT_STREQ(e.what(), "Element is not a string");
5075     }
5076 
5077     // Test where fails: Empty string
5078     try
5079     {
5080         const json element = "";
5081         parseSetDevice(element);
5082         ADD_FAILURE() << "Should not have reached this line.";
5083     }
5084     catch (const std::invalid_argument& e)
5085     {
5086         EXPECT_STREQ(e.what(), "Element contains an empty string");
5087     }
5088 }
5089 
5090 TEST(ConfigFileParserTests, ParseString)
5091 {
5092     // Test where works: Empty string
5093     {
5094         const json element = "";
5095         std::string value = parseString(element, true);
5096         EXPECT_EQ(value, "");
5097     }
5098 
5099     // Test where works: Non-empty string
5100     {
5101         const json element = "vdd_regulator";
5102         std::string value = parseString(element, false);
5103         EXPECT_EQ(value, "vdd_regulator");
5104     }
5105 
5106     // Test where fails: Element is not a string
5107     try
5108     {
5109         const json element = R"( { "foo": "bar" } )"_json;
5110         parseString(element);
5111         ADD_FAILURE() << "Should not have reached this line.";
5112     }
5113     catch (const std::invalid_argument& e)
5114     {
5115         EXPECT_STREQ(e.what(), "Element is not a string");
5116     }
5117 
5118     // Test where fails: Empty string
5119     try
5120     {
5121         const json element = "";
5122         parseString(element);
5123         ADD_FAILURE() << "Should not have reached this line.";
5124     }
5125     catch (const std::invalid_argument& e)
5126     {
5127         EXPECT_STREQ(e.what(), "Element contains an empty string");
5128     }
5129 }
5130 
5131 TEST(ConfigFileParserTests, ParseUint8)
5132 {
5133     // Test where works: 0
5134     {
5135         const json element = R"( 0 )"_json;
5136         uint8_t value = parseUint8(element);
5137         EXPECT_EQ(value, 0);
5138     }
5139 
5140     // Test where works: UINT8_MAX
5141     {
5142         const json element = R"( 255 )"_json;
5143         uint8_t value = parseUint8(element);
5144         EXPECT_EQ(value, 255);
5145     }
5146 
5147     // Test where fails: Element is not an integer
5148     try
5149     {
5150         const json element = R"( 1.03 )"_json;
5151         parseUint8(element);
5152         ADD_FAILURE() << "Should not have reached this line.";
5153     }
5154     catch (const std::invalid_argument& e)
5155     {
5156         EXPECT_STREQ(e.what(), "Element is not an integer");
5157     }
5158 
5159     // Test where fails: Value < 0
5160     try
5161     {
5162         const json element = R"( -1 )"_json;
5163         parseUint8(element);
5164         ADD_FAILURE() << "Should not have reached this line.";
5165     }
5166     catch (const std::invalid_argument& e)
5167     {
5168         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
5169     }
5170 
5171     // Test where fails: Value > UINT8_MAX
5172     try
5173     {
5174         const json element = R"( 256 )"_json;
5175         parseUint8(element);
5176         ADD_FAILURE() << "Should not have reached this line.";
5177     }
5178     catch (const std::invalid_argument& e)
5179     {
5180         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
5181     }
5182 }
5183 
5184 TEST(ConfigFileParserTests, ParseUnsignedInteger)
5185 {
5186     // Test where works: 1
5187     {
5188         const json element = R"( 1 )"_json;
5189         unsigned int value = parseUnsignedInteger(element);
5190         EXPECT_EQ(value, 1);
5191     }
5192 
5193     // Test where fails: Element is not an integer
5194     try
5195     {
5196         const json element = R"( 1.5 )"_json;
5197         parseUnsignedInteger(element);
5198         ADD_FAILURE() << "Should not have reached this line.";
5199     }
5200     catch (const std::invalid_argument& e)
5201     {
5202         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
5203     }
5204 
5205     // Test where fails: Value < 0
5206     try
5207     {
5208         const json element = R"( -1 )"_json;
5209         parseUnsignedInteger(element);
5210         ADD_FAILURE() << "Should not have reached this line.";
5211     }
5212     catch (const std::invalid_argument& e)
5213     {
5214         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
5215     }
5216 }
5217 
5218 TEST(ConfigFileParserTests, ParseVoutDataFormat)
5219 {
5220     // Test where works: linear
5221     {
5222         const json element = "linear";
5223         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5224         pmbus_utils::VoutDataFormat format =
5225             pmbus_utils::VoutDataFormat::linear;
5226         EXPECT_EQ(value, format);
5227     }
5228 
5229     // Test where works: vid
5230     {
5231         const json element = "vid";
5232         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5233         pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::vid;
5234         EXPECT_EQ(value, format);
5235     }
5236 
5237     // Test where works: direct
5238     {
5239         const json element = "direct";
5240         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5241         pmbus_utils::VoutDataFormat format =
5242             pmbus_utils::VoutDataFormat::direct;
5243         EXPECT_EQ(value, format);
5244     }
5245 
5246     // Test where works: ieee
5247     {
5248         const json element = "ieee";
5249         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5250         pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::ieee;
5251         EXPECT_EQ(value, format);
5252     }
5253 
5254     // Test where fails: Element is not a vout data format
5255     try
5256     {
5257         const json element = "foo";
5258         parseVoutDataFormat(element);
5259         ADD_FAILURE() << "Should not have reached this line.";
5260     }
5261     catch (const std::invalid_argument& e)
5262     {
5263         EXPECT_STREQ(e.what(), "Element is not a vout data format");
5264     }
5265 
5266     // Test where fails: Element is not a string
5267     try
5268     {
5269         const json element = R"( { "foo": "bar" } )"_json;
5270         parseVoutDataFormat(element);
5271         ADD_FAILURE() << "Should not have reached this line.";
5272     }
5273     catch (const std::invalid_argument& e)
5274     {
5275         EXPECT_STREQ(e.what(), "Element is not a string");
5276     }
5277 }
5278 
5279 TEST(ConfigFileParserTests, VerifyIsArray)
5280 {
5281     // Test where element is an array
5282     try
5283     {
5284         const json element = R"( [ "foo", "bar" ] )"_json;
5285         verifyIsArray(element);
5286     }
5287     catch (const std::exception& e)
5288     {
5289         ADD_FAILURE() << "Should not have caught exception.";
5290     }
5291 
5292     // Test where element is not an array
5293     try
5294     {
5295         const json element = R"( { "foo": "bar" } )"_json;
5296         verifyIsArray(element);
5297         ADD_FAILURE() << "Should not have reached this line.";
5298     }
5299     catch (const std::invalid_argument& e)
5300     {
5301         EXPECT_STREQ(e.what(), "Element is not an array");
5302     }
5303 }
5304 
5305 TEST(ConfigFileParserTests, VerifyIsObject)
5306 {
5307     // Test where element is an object
5308     try
5309     {
5310         const json element = R"( { "foo": "bar" } )"_json;
5311         verifyIsObject(element);
5312     }
5313     catch (const std::exception& e)
5314     {
5315         ADD_FAILURE() << "Should not have caught exception.";
5316     }
5317 
5318     // Test where element is not an object
5319     try
5320     {
5321         const json element = R"( [ "foo", "bar" ] )"_json;
5322         verifyIsObject(element);
5323         ADD_FAILURE() << "Should not have reached this line.";
5324     }
5325     catch (const std::invalid_argument& e)
5326     {
5327         EXPECT_STREQ(e.what(), "Element is not an object");
5328     }
5329 }
5330 
5331 TEST(ConfigFileParserTests, VerifyPropertyCount)
5332 {
5333     // Test where element has expected number of properties
5334     try
5335     {
5336         const json element = R"(
5337             {
5338               "comments": [ "Set voltage rule" ],
5339               "id": "set_voltage_rule"
5340             }
5341         )"_json;
5342         verifyPropertyCount(element, 2);
5343     }
5344     catch (const std::exception& e)
5345     {
5346         ADD_FAILURE() << "Should not have caught exception.";
5347     }
5348 
5349     // Test where element has unexpected number of properties
5350     try
5351     {
5352         const json element = R"(
5353             {
5354               "comments": [ "Set voltage rule" ],
5355               "id": "set_voltage_rule",
5356               "foo": 1.3
5357             }
5358         )"_json;
5359         verifyPropertyCount(element, 2);
5360         ADD_FAILURE() << "Should not have reached this line.";
5361     }
5362     catch (const std::invalid_argument& e)
5363     {
5364         EXPECT_STREQ(e.what(), "Element contains an invalid property");
5365     }
5366 }
5367