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->getPhaseFaultDetection(), nullptr);
1536         EXPECT_EQ(device->getRails().size(), 0);
1537     }
1538 
1539     // Test where works: All properties specified
1540     {
1541         const json element = R"(
1542             {
1543               "comments": [ "VDD Regulator" ],
1544               "id": "vdd_regulator",
1545               "is_regulator": true,
1546               "fru": "system/chassis/motherboard/regulator2",
1547               "i2c_interface":
1548               {
1549                   "bus": 1,
1550                   "address": "0x70"
1551               },
1552               "presence_detection":
1553               {
1554                   "rule_id": "is_foobar_backplane_installed_rule"
1555               },
1556               "configuration":
1557               {
1558                   "rule_id": "configure_ir35221_rule"
1559               },
1560               "phase_fault_detection":
1561               {
1562                   "rule_id": "detect_phase_fault_rule"
1563               },
1564               "rails":
1565               [
1566                 {
1567                   "id": "vdd"
1568                 }
1569               ]
1570             }
1571         )"_json;
1572         std::unique_ptr<Device> device = parseDevice(element);
1573         EXPECT_EQ(device->getID(), "vdd_regulator");
1574         EXPECT_EQ(device->isRegulator(), true);
1575         EXPECT_EQ(device->getFRU(), "/xyz/openbmc_project/inventory/system/"
1576                                     "chassis/motherboard/regulator2");
1577         EXPECT_NE(&(device->getI2CInterface()), nullptr);
1578         EXPECT_NE(device->getPresenceDetection(), nullptr);
1579         EXPECT_NE(device->getConfiguration(), nullptr);
1580         EXPECT_NE(device->getPhaseFaultDetection(), nullptr);
1581         EXPECT_EQ(device->getRails().size(), 1);
1582     }
1583 
1584     // Test where fails: phase_fault_detection property exists and is_regulator
1585     // is false
1586     try
1587     {
1588         const json element = R"(
1589             {
1590               "id": "vdd_regulator",
1591               "is_regulator": false,
1592               "fru": "system/chassis/motherboard/regulator2",
1593               "i2c_interface":
1594               {
1595                   "bus": 1,
1596                   "address": "0x70"
1597               },
1598               "phase_fault_detection":
1599               {
1600                   "rule_id": "detect_phase_fault_rule"
1601               }
1602             }
1603         )"_json;
1604         parseDevice(element);
1605         ADD_FAILURE() << "Should not have reached this line.";
1606     }
1607     catch (const std::invalid_argument& e)
1608     {
1609         EXPECT_STREQ(e.what(), "Invalid phase_fault_detection property when "
1610                                "is_regulator is false");
1611     }
1612 
1613     // Test where fails: rails property exists and is_regulator is false
1614     try
1615     {
1616         const json element = R"(
1617             {
1618               "id": "vdd_regulator",
1619               "is_regulator": false,
1620               "fru": "system/chassis/motherboard/regulator2",
1621               "i2c_interface":
1622               {
1623                   "bus": 1,
1624                   "address": "0x70"
1625               },
1626               "configuration":
1627               {
1628                   "rule_id": "configure_ir35221_rule"
1629               },
1630               "rails":
1631               [
1632                 {
1633                   "id": "vdd"
1634                 }
1635               ]
1636             }
1637         )"_json;
1638         parseDevice(element);
1639         ADD_FAILURE() << "Should not have reached this line.";
1640     }
1641     catch (const std::invalid_argument& e)
1642     {
1643         EXPECT_STREQ(e.what(),
1644                      "Invalid rails property when is_regulator is false");
1645     }
1646 
1647     // Test where fails: id value is invalid
1648     try
1649     {
1650         const json element = R"(
1651             {
1652               "id": 3,
1653               "is_regulator": true,
1654               "fru": "system/chassis/motherboard/regulator2",
1655               "i2c_interface":
1656               {
1657                   "bus": 1,
1658                   "address": "0x70"
1659               }
1660             }
1661         )"_json;
1662         parseDevice(element);
1663         ADD_FAILURE() << "Should not have reached this line.";
1664     }
1665     catch (const std::invalid_argument& e)
1666     {
1667         EXPECT_STREQ(e.what(), "Element is not a string");
1668     }
1669 
1670     // Test where fails: is_regulator value is invalid
1671     try
1672     {
1673         const json element = R"(
1674             {
1675               "id": "vdd_regulator",
1676               "is_regulator": 3,
1677               "fru": "system/chassis/motherboard/regulator2",
1678               "i2c_interface":
1679               {
1680                   "bus": 1,
1681                   "address": "0x70"
1682               }
1683             }
1684         )"_json;
1685         parseDevice(element);
1686         ADD_FAILURE() << "Should not have reached this line.";
1687     }
1688     catch (const std::invalid_argument& e)
1689     {
1690         EXPECT_STREQ(e.what(), "Element is not a boolean");
1691     }
1692 
1693     // Test where fails: fru value is invalid
1694     try
1695     {
1696         const json element = R"(
1697             {
1698               "id": "vdd_regulator",
1699               "is_regulator": true,
1700               "fru": 2,
1701               "i2c_interface":
1702               {
1703                   "bus": 1,
1704                   "address": "0x70"
1705               }
1706             }
1707         )"_json;
1708         parseDevice(element);
1709         ADD_FAILURE() << "Should not have reached this line.";
1710     }
1711     catch (const std::invalid_argument& e)
1712     {
1713         EXPECT_STREQ(e.what(), "Element is not a string");
1714     }
1715 
1716     // Test where fails: i2c_interface value is invalid
1717     try
1718     {
1719         const json element = R"(
1720             {
1721               "id": "vdd_regulator",
1722               "is_regulator": true,
1723               "fru": "system/chassis/motherboard/regulator2",
1724               "i2c_interface": 3
1725             }
1726         )"_json;
1727         parseDevice(element);
1728         ADD_FAILURE() << "Should not have reached this line.";
1729     }
1730     catch (const std::invalid_argument& e)
1731     {
1732         EXPECT_STREQ(e.what(), "Element is not an object");
1733     }
1734 
1735     // Test where fails: Required id property not specified
1736     try
1737     {
1738         const json element = R"(
1739             {
1740               "is_regulator": true,
1741               "fru": "system/chassis/motherboard/regulator2",
1742               "i2c_interface":
1743               {
1744                   "bus": 1,
1745                   "address": "0x70"
1746               }
1747             }
1748         )"_json;
1749         parseDevice(element);
1750         ADD_FAILURE() << "Should not have reached this line.";
1751     }
1752     catch (const std::invalid_argument& e)
1753     {
1754         EXPECT_STREQ(e.what(), "Required property missing: id");
1755     }
1756 
1757     // Test where fails: Required is_regulator property not specified
1758     try
1759     {
1760         const json element = R"(
1761             {
1762               "id": "vdd_regulator",
1763               "fru": "system/chassis/motherboard/regulator2",
1764               "i2c_interface":
1765               {
1766                   "bus": 1,
1767                   "address": "0x70"
1768               }
1769             }
1770         )"_json;
1771         parseDevice(element);
1772         ADD_FAILURE() << "Should not have reached this line.";
1773     }
1774     catch (const std::invalid_argument& e)
1775     {
1776         EXPECT_STREQ(e.what(), "Required property missing: is_regulator");
1777     }
1778 
1779     // Test where fails: Required fru property not specified
1780     try
1781     {
1782         const json element = R"(
1783             {
1784               "id": "vdd_regulator",
1785               "is_regulator": true,
1786               "i2c_interface":
1787               {
1788                   "bus": 1,
1789                   "address": "0x70"
1790               }
1791             }
1792         )"_json;
1793         parseDevice(element);
1794         ADD_FAILURE() << "Should not have reached this line.";
1795     }
1796     catch (const std::invalid_argument& e)
1797     {
1798         EXPECT_STREQ(e.what(), "Required property missing: fru");
1799     }
1800 
1801     // Test where fails: Required i2c_interface property not specified
1802     try
1803     {
1804         const json element = R"(
1805             {
1806               "id": "vdd_regulator",
1807               "is_regulator": true,
1808               "fru": "system/chassis/motherboard/regulator2"
1809             }
1810         )"_json;
1811         parseDevice(element);
1812         ADD_FAILURE() << "Should not have reached this line.";
1813     }
1814     catch (const std::invalid_argument& e)
1815     {
1816         EXPECT_STREQ(e.what(), "Required property missing: i2c_interface");
1817     }
1818 
1819     // Test where fails: Element is not an object
1820     try
1821     {
1822         const json element = R"( [ "0xFF", "0x01" ] )"_json;
1823         parseDevice(element);
1824         ADD_FAILURE() << "Should not have reached this line.";
1825     }
1826     catch (const std::invalid_argument& e)
1827     {
1828         EXPECT_STREQ(e.what(), "Element is not an object");
1829     }
1830 
1831     // Test where fails: Invalid property specified
1832     try
1833     {
1834         const json element = R"(
1835             {
1836               "id": "vdd_regulator",
1837               "is_regulator": true,
1838               "fru": "system/chassis/motherboard/regulator2",
1839               "i2c_interface": { "bus": 1, "address": "0x70" },
1840               "foo" : true
1841             }
1842         )"_json;
1843         parseDevice(element);
1844         ADD_FAILURE() << "Should not have reached this line.";
1845     }
1846     catch (const std::invalid_argument& e)
1847     {
1848         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1849     }
1850 }
1851 
1852 TEST(ConfigFileParserTests, ParseDeviceArray)
1853 {
1854     // Test where works
1855     {
1856         const json element = R"(
1857             [
1858               {
1859                 "id": "vdd_regulator",
1860                 "is_regulator": true,
1861                 "fru": "system/chassis/motherboard/regulator2",
1862                 "i2c_interface": { "bus": 1, "address": "0x70" }
1863               },
1864               {
1865                 "id": "vio_regulator",
1866                 "is_regulator": true,
1867                 "fru": "system/chassis/motherboard/regulator2",
1868                 "i2c_interface": { "bus": 1, "address": "0x71" }
1869               }
1870             ]
1871         )"_json;
1872         std::vector<std::unique_ptr<Device>> devices =
1873             parseDeviceArray(element);
1874         EXPECT_EQ(devices.size(), 2);
1875         EXPECT_EQ(devices[0]->getID(), "vdd_regulator");
1876         EXPECT_EQ(devices[1]->getID(), "vio_regulator");
1877     }
1878 
1879     // Test where fails: Element is not an array
1880     try
1881     {
1882         const json element = R"(
1883             {
1884               "foo": "bar"
1885             }
1886         )"_json;
1887         parseDeviceArray(element);
1888         ADD_FAILURE() << "Should not have reached this line.";
1889     }
1890     catch (const std::invalid_argument& e)
1891     {
1892         EXPECT_STREQ(e.what(), "Element is not an array");
1893     }
1894 }
1895 
1896 TEST(ConfigFileParserTests, ParseDouble)
1897 {
1898     // Test where works: floating point value
1899     {
1900         const json element = R"( 1.03 )"_json;
1901         double value = parseDouble(element);
1902         EXPECT_EQ(value, 1.03);
1903     }
1904 
1905     // Test where works: integer value
1906     {
1907         const json element = R"( 24 )"_json;
1908         double value = parseDouble(element);
1909         EXPECT_EQ(value, 24.0);
1910     }
1911 
1912     // Test where fails: Element is not a number
1913     try
1914     {
1915         const json element = R"( true )"_json;
1916         parseDouble(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 a number");
1922     }
1923 }
1924 
1925 TEST(ConfigFileParserTests, ParseHexByte)
1926 {
1927     // Test where works: "0xFF"
1928     {
1929         const json element = R"( "0xFF" )"_json;
1930         uint8_t value = parseHexByte(element);
1931         EXPECT_EQ(value, 0xFF);
1932     }
1933 
1934     // Test where works: "0xff"
1935     {
1936         const json element = R"( "0xff" )"_json;
1937         uint8_t value = parseHexByte(element);
1938         EXPECT_EQ(value, 0xff);
1939     }
1940 
1941     // Test where works: "0xf"
1942     {
1943         const json element = R"( "0xf" )"_json;
1944         uint8_t value = parseHexByte(element);
1945         EXPECT_EQ(value, 0xf);
1946     }
1947 
1948     // Test where fails: "0xfff"
1949     try
1950     {
1951         const json element = R"( "0xfff" )"_json;
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: "0xAG"
1961     try
1962     {
1963         const json element = R"( "0xAG" )"_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: "ff"
1973     try
1974     {
1975         const json element = R"( "ff" )"_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: ""
1985     try
1986     {
1987         const json element = "";
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     // Test where fails: "f"
1997     try
1998     {
1999         const json element = R"( "f" )"_json;
2000         parseHexByte(element);
2001         ADD_FAILURE() << "Should not have reached this line.";
2002     }
2003     catch (const std::invalid_argument& e)
2004     {
2005         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2006     }
2007 
2008     // Test where fails: "0x"
2009     try
2010     {
2011         const json element = R"( "0x" )"_json;
2012         parseHexByte(element);
2013         ADD_FAILURE() << "Should not have reached this line.";
2014     }
2015     catch (const std::invalid_argument& e)
2016     {
2017         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2018     }
2019 
2020     // Test where fails: "0Xff"
2021     try
2022     {
2023         const json element = R"( "0XFF" )"_json;
2024         parseHexByte(element);
2025         ADD_FAILURE() << "Should not have reached this line.";
2026     }
2027     catch (const std::invalid_argument& e)
2028     {
2029         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2030     }
2031 }
2032 
2033 TEST(ConfigFileParserTests, ParseHexByteArray)
2034 {
2035     // Test where works
2036     {
2037         const json element = R"( [ "0xCC", "0xFF" ] )"_json;
2038         std::vector<uint8_t> hexBytes = parseHexByteArray(element);
2039         std::vector<uint8_t> expected = {0xcc, 0xff};
2040         EXPECT_EQ(hexBytes, expected);
2041     }
2042 
2043     // Test where fails: Element is not an array
2044     try
2045     {
2046         const json element = 0;
2047         parseHexByteArray(element);
2048         ADD_FAILURE() << "Should not have reached this line.";
2049     }
2050     catch (const std::invalid_argument& e)
2051     {
2052         EXPECT_STREQ(e.what(), "Element is not an array");
2053     }
2054 }
2055 
2056 TEST(ConfigFileParserTests, ParseI2CCaptureBytes)
2057 {
2058     // Test where works
2059     {
2060         const json element = R"(
2061             {
2062               "register": "0xA0",
2063               "count": 2
2064             }
2065         )"_json;
2066         std::unique_ptr<I2CCaptureBytesAction> action =
2067             parseI2CCaptureBytes(element);
2068         EXPECT_EQ(action->getRegister(), 0xA0);
2069         EXPECT_EQ(action->getCount(), 2);
2070     }
2071 
2072     // Test where fails: Element is not an object
2073     try
2074     {
2075         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2076         parseI2CCaptureBytes(element);
2077         ADD_FAILURE() << "Should not have reached this line.";
2078     }
2079     catch (const std::invalid_argument& e)
2080     {
2081         EXPECT_STREQ(e.what(), "Element is not an object");
2082     }
2083 
2084     // Test where fails: register value is invalid
2085     try
2086     {
2087         const json element = R"(
2088             {
2089               "register": "0x0Z",
2090               "count": 2
2091             }
2092         )"_json;
2093         parseI2CCaptureBytes(element);
2094         ADD_FAILURE() << "Should not have reached this line.";
2095     }
2096     catch (const std::invalid_argument& e)
2097     {
2098         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2099     }
2100 
2101     // Test where fails: count value is invalid
2102     try
2103     {
2104         const json element = R"(
2105             {
2106               "register": "0xA0",
2107               "count": 0
2108             }
2109         )"_json;
2110         parseI2CCaptureBytes(element);
2111         ADD_FAILURE() << "Should not have reached this line.";
2112     }
2113     catch (const std::invalid_argument& e)
2114     {
2115         EXPECT_STREQ(e.what(), "Invalid byte count: Must be > 0");
2116     }
2117 
2118     // Test where fails: Required register property not specified
2119     try
2120     {
2121         const json element = R"(
2122             {
2123               "count": 2
2124             }
2125         )"_json;
2126         parseI2CCaptureBytes(element);
2127         ADD_FAILURE() << "Should not have reached this line.";
2128     }
2129     catch (const std::invalid_argument& e)
2130     {
2131         EXPECT_STREQ(e.what(), "Required property missing: register");
2132     }
2133 
2134     // Test where fails: Required count property not specified
2135     try
2136     {
2137         const json element = R"(
2138             {
2139               "register": "0xA0"
2140             }
2141         )"_json;
2142         parseI2CCaptureBytes(element);
2143         ADD_FAILURE() << "Should not have reached this line.";
2144     }
2145     catch (const std::invalid_argument& e)
2146     {
2147         EXPECT_STREQ(e.what(), "Required property missing: count");
2148     }
2149 
2150     // Test where fails: Invalid property specified
2151     try
2152     {
2153         const json element = R"(
2154             {
2155               "register": "0xA0",
2156               "count": 2,
2157               "foo": 3
2158             }
2159         )"_json;
2160         parseI2CCaptureBytes(element);
2161         ADD_FAILURE() << "Should not have reached this line.";
2162     }
2163     catch (const std::invalid_argument& e)
2164     {
2165         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2166     }
2167 }
2168 
2169 TEST(ConfigFileParserTests, ParseI2CCompareBit)
2170 {
2171     // Test where works
2172     {
2173         const json element = R"(
2174             {
2175               "register": "0xA0",
2176               "position": 3,
2177               "value": 0
2178             }
2179         )"_json;
2180         std::unique_ptr<I2CCompareBitAction> action =
2181             parseI2CCompareBit(element);
2182         EXPECT_EQ(action->getRegister(), 0xA0);
2183         EXPECT_EQ(action->getPosition(), 3);
2184         EXPECT_EQ(action->getValue(), 0);
2185     }
2186 
2187     // Test where fails: Invalid property specified
2188     try
2189     {
2190         const json element = R"(
2191             {
2192               "register": "0xA0",
2193               "position": 3,
2194               "value": 0,
2195               "foo": 3
2196             }
2197         )"_json;
2198         parseI2CCompareBit(element);
2199         ADD_FAILURE() << "Should not have reached this line.";
2200     }
2201     catch (const std::invalid_argument& e)
2202     {
2203         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2204     }
2205 
2206     // Test where fails: Element is not an object
2207     try
2208     {
2209         const json element = R"( [ "0xFF", "0x01" ] )"_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 an object");
2216     }
2217 
2218     // Test where fails: register value is invalid
2219     try
2220     {
2221         const json element = R"(
2222             {
2223               "register": "0xAG",
2224               "position": 3,
2225               "value": 0
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 hexadecimal string");
2234     }
2235 
2236     // Test where fails: position value is invalid
2237     try
2238     {
2239         const json element = R"(
2240                 {
2241                   "register": "0xA0",
2242                   "position": 8,
2243                   "value": 0
2244                 }
2245             )"_json;
2246         parseI2CCompareBit(element);
2247         ADD_FAILURE() << "Should not have reached this line.";
2248     }
2249     catch (const std::invalid_argument& e)
2250     {
2251         EXPECT_STREQ(e.what(), "Element is not a bit position");
2252     }
2253 
2254     // Test where fails: value value is invalid
2255     try
2256     {
2257         const json element = R"(
2258                 {
2259                   "register": "0xA0",
2260                   "position": 3,
2261                   "value": 2
2262                 }
2263             )"_json;
2264         parseI2CCompareBit(element);
2265         ADD_FAILURE() << "Should not have reached this line.";
2266     }
2267     catch (const std::invalid_argument& e)
2268     {
2269         EXPECT_STREQ(e.what(), "Element is not a bit value");
2270     }
2271 
2272     // Test where fails: Required register property not specified
2273     try
2274     {
2275         const json element = R"(
2276             {
2277               "position": 3,
2278               "value": 0
2279             }
2280         )"_json;
2281         parseI2CCompareBit(element);
2282         ADD_FAILURE() << "Should not have reached this line.";
2283     }
2284     catch (const std::invalid_argument& e)
2285     {
2286         EXPECT_STREQ(e.what(), "Required property missing: register");
2287     }
2288 
2289     // Test where fails: Required position property not specified
2290     try
2291     {
2292         const json element = R"(
2293             {
2294               "register": "0xA0",
2295               "value": 0
2296             }
2297         )"_json;
2298         parseI2CCompareBit(element);
2299         ADD_FAILURE() << "Should not have reached this line.";
2300     }
2301     catch (const std::invalid_argument& e)
2302     {
2303         EXPECT_STREQ(e.what(), "Required property missing: position");
2304     }
2305 
2306     // Test where fails: Required value property not specified
2307     try
2308     {
2309         const json element = R"(
2310             {
2311               "register": "0xA0",
2312               "position": 3
2313             }
2314         )"_json;
2315         parseI2CCompareBit(element);
2316         ADD_FAILURE() << "Should not have reached this line.";
2317     }
2318     catch (const std::invalid_argument& e)
2319     {
2320         EXPECT_STREQ(e.what(), "Required property missing: value");
2321     }
2322 }
2323 
2324 TEST(ConfigFileParserTests, ParseI2CCompareByte)
2325 {
2326     // Test where works: Only required properties specified
2327     {
2328         const json element = R"(
2329             {
2330               "register": "0x0A",
2331               "value": "0xCC"
2332             }
2333         )"_json;
2334         std::unique_ptr<I2CCompareByteAction> action =
2335             parseI2CCompareByte(element);
2336         EXPECT_EQ(action->getRegister(), 0x0A);
2337         EXPECT_EQ(action->getValue(), 0xCC);
2338         EXPECT_EQ(action->getMask(), 0xFF);
2339     }
2340 
2341     // Test where works: All properties specified
2342     {
2343         const json element = R"(
2344             {
2345               "register": "0x0A",
2346               "value": "0xCC",
2347               "mask": "0xF7"
2348             }
2349         )"_json;
2350         std::unique_ptr<I2CCompareByteAction> action =
2351             parseI2CCompareByte(element);
2352         EXPECT_EQ(action->getRegister(), 0x0A);
2353         EXPECT_EQ(action->getValue(), 0xCC);
2354         EXPECT_EQ(action->getMask(), 0xF7);
2355     }
2356 
2357     // Test where fails: Element is not an object
2358     try
2359     {
2360         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2361         parseI2CCompareByte(element);
2362         ADD_FAILURE() << "Should not have reached this line.";
2363     }
2364     catch (const std::invalid_argument& e)
2365     {
2366         EXPECT_STREQ(e.what(), "Element is not an object");
2367     }
2368 
2369     // Test where fails: Invalid property specified
2370     try
2371     {
2372         const json element = R"(
2373             {
2374               "register": "0x0A",
2375               "value": "0xCC",
2376               "mask": "0xF7",
2377               "foo": 1
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 contains an invalid property");
2386     }
2387 
2388     // Test where fails: register value is invalid
2389     try
2390     {
2391         const json element = R"(
2392             {
2393               "register": "0x0Z",
2394               "value": "0xCC",
2395               "mask": "0xF7"
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: value value is invalid
2407     try
2408     {
2409         const json element = R"(
2410             {
2411               "register": "0x0A",
2412               "value": "0xCCC",
2413               "mask": "0xF7"
2414             }
2415         )"_json;
2416         parseI2CCompareByte(element);
2417         ADD_FAILURE() << "Should not have reached this line.";
2418     }
2419     catch (const std::invalid_argument& e)
2420     {
2421         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2422     }
2423 
2424     // Test where fails: mask value is invalid
2425     try
2426     {
2427         const json element = R"(
2428             {
2429               "register": "0x0A",
2430               "value": "0xCC",
2431               "mask": "F7"
2432             }
2433         )"_json;
2434         parseI2CCompareByte(element);
2435         ADD_FAILURE() << "Should not have reached this line.";
2436     }
2437     catch (const std::invalid_argument& e)
2438     {
2439         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2440     }
2441 
2442     // Test where fails: Required register property not specified
2443     try
2444     {
2445         const json element = R"(
2446             {
2447               "value": "0xCC",
2448               "mask": "0xF7"
2449             }
2450         )"_json;
2451         parseI2CCompareByte(element);
2452         ADD_FAILURE() << "Should not have reached this line.";
2453     }
2454     catch (const std::invalid_argument& e)
2455     {
2456         EXPECT_STREQ(e.what(), "Required property missing: register");
2457     }
2458 
2459     // Test where fails: Required value property not specified
2460     try
2461     {
2462         const json element = R"(
2463             {
2464               "register": "0x0A",
2465               "mask": "0xF7"
2466             }
2467         )"_json;
2468         parseI2CCompareByte(element);
2469         ADD_FAILURE() << "Should not have reached this line.";
2470     }
2471     catch (const std::invalid_argument& e)
2472     {
2473         EXPECT_STREQ(e.what(), "Required property missing: value");
2474     }
2475 }
2476 
2477 TEST(ConfigFileParserTests, ParseI2CCompareBytes)
2478 {
2479     // Test where works: Only required properties specified
2480     {
2481         const json element = R"(
2482             {
2483               "register": "0x0A",
2484               "values": [ "0xCC", "0xFF" ]
2485             }
2486         )"_json;
2487         std::unique_ptr<I2CCompareBytesAction> action =
2488             parseI2CCompareBytes(element);
2489         EXPECT_EQ(action->getRegister(), 0x0A);
2490         EXPECT_EQ(action->getValues().size(), 2);
2491         EXPECT_EQ(action->getValues()[0], 0xCC);
2492         EXPECT_EQ(action->getValues()[1], 0xFF);
2493         EXPECT_EQ(action->getMasks().size(), 2);
2494         EXPECT_EQ(action->getMasks()[0], 0xFF);
2495         EXPECT_EQ(action->getMasks()[1], 0xFF);
2496     }
2497 
2498     // Test where works: All properties specified
2499     {
2500         const json element = R"(
2501             {
2502               "register": "0x0A",
2503               "values": [ "0xCC", "0xFF" ],
2504               "masks":  [ "0x7F", "0x77" ]
2505             }
2506         )"_json;
2507         std::unique_ptr<I2CCompareBytesAction> action =
2508             parseI2CCompareBytes(element);
2509         EXPECT_EQ(action->getRegister(), 0x0A);
2510         EXPECT_EQ(action->getValues().size(), 2);
2511         EXPECT_EQ(action->getValues()[0], 0xCC);
2512         EXPECT_EQ(action->getValues()[1], 0xFF);
2513         EXPECT_EQ(action->getMasks().size(), 2);
2514         EXPECT_EQ(action->getMasks()[0], 0x7F);
2515         EXPECT_EQ(action->getMasks()[1], 0x77);
2516     }
2517 
2518     // Test where fails: Element is not an object
2519     try
2520     {
2521         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2522         parseI2CCompareBytes(element);
2523         ADD_FAILURE() << "Should not have reached this line.";
2524     }
2525     catch (const std::invalid_argument& e)
2526     {
2527         EXPECT_STREQ(e.what(), "Element is not an object");
2528     }
2529 
2530     // Test where fails: Invalid property specified
2531     try
2532     {
2533         const json element = R"(
2534             {
2535               "register": "0x0A",
2536               "values": [ "0xCC", "0xFF" ],
2537               "masks":  [ "0x7F", "0x7F" ],
2538               "foo": 1
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 contains an invalid property");
2547     }
2548 
2549     // Test where fails: register value is invalid
2550     try
2551     {
2552         const json element = R"(
2553             {
2554               "register": "0x0Z",
2555               "values": [ "0xCC", "0xFF" ],
2556               "masks":  [ "0x7F", "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: values value is invalid
2568     try
2569     {
2570         const json element = R"(
2571             {
2572               "register": "0x0A",
2573               "values": [ "0xCCC", "0xFF" ],
2574               "masks":  [ "0x7F", "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(), "Element is not hexadecimal string");
2583     }
2584 
2585     // Test where fails: masks value is invalid
2586     try
2587     {
2588         const json element = R"(
2589             {
2590               "register": "0x0A",
2591               "values": [ "0xCC", "0xFF" ],
2592               "masks":  [ "F", "0x7F" ]
2593             }
2594         )"_json;
2595         parseI2CCompareBytes(element);
2596         ADD_FAILURE() << "Should not have reached this line.";
2597     }
2598     catch (const std::invalid_argument& e)
2599     {
2600         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2601     }
2602 
2603     // Test where fails: number of elements in masks is invalid
2604     try
2605     {
2606         const json element = R"(
2607             {
2608               "register": "0x0A",
2609               "values": [ "0xCC", "0xFF" ],
2610               "masks":  [ "0x7F" ]
2611             }
2612         )"_json;
2613         parseI2CCompareBytes(element);
2614         ADD_FAILURE() << "Should not have reached this line.";
2615     }
2616     catch (const std::invalid_argument& e)
2617     {
2618         EXPECT_STREQ(e.what(), "Invalid number of elements in masks");
2619     }
2620 
2621     // Test where fails: Required register property not specified
2622     try
2623     {
2624         const json element = R"(
2625             {
2626               "values": [ "0xCC", "0xFF" ]
2627             }
2628         )"_json;
2629         parseI2CCompareBytes(element);
2630         ADD_FAILURE() << "Should not have reached this line.";
2631     }
2632     catch (const std::invalid_argument& e)
2633     {
2634         EXPECT_STREQ(e.what(), "Required property missing: register");
2635     }
2636 
2637     // Test where fails: Required values property not specified
2638     try
2639     {
2640         const json element = R"(
2641             {
2642               "register": "0x0A"
2643             }
2644         )"_json;
2645         parseI2CCompareBytes(element);
2646         ADD_FAILURE() << "Should not have reached this line.";
2647     }
2648     catch (const std::invalid_argument& e)
2649     {
2650         EXPECT_STREQ(e.what(), "Required property missing: values");
2651     }
2652 }
2653 
2654 TEST(ConfigFileParserTests, ParseI2CWriteBit)
2655 {
2656     // Test where works
2657     {
2658         const json element = R"(
2659             {
2660               "register": "0xA0",
2661               "position": 3,
2662               "value": 0
2663             }
2664         )"_json;
2665         std::unique_ptr<I2CWriteBitAction> action = parseI2CWriteBit(element);
2666         EXPECT_EQ(action->getRegister(), 0xA0);
2667         EXPECT_EQ(action->getPosition(), 3);
2668         EXPECT_EQ(action->getValue(), 0);
2669     }
2670 
2671     // Test where fails: Invalid property specified
2672     try
2673     {
2674         const json element = R"(
2675             {
2676               "register": "0xA0",
2677               "position": 3,
2678               "value": 0,
2679               "foo": 3
2680             }
2681         )"_json;
2682         parseI2CWriteBit(element);
2683         ADD_FAILURE() << "Should not have reached this line.";
2684     }
2685     catch (const std::invalid_argument& e)
2686     {
2687         EXPECT_STREQ(e.what(), "Element contains an invalid property");
2688     }
2689 
2690     // Test where fails: Element is not an object
2691     try
2692     {
2693         const json element = R"( [ "0xFF", "0x01" ] )"_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 an object");
2700     }
2701 
2702     // Test where fails: register value is invalid
2703     try
2704     {
2705         const json element = R"(
2706             {
2707               "register": "0xAG",
2708               "position": 3,
2709               "value": 0
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 hexadecimal string");
2718     }
2719 
2720     // Test where fails: position value is invalid
2721     try
2722     {
2723         const json element = R"(
2724                 {
2725                   "register": "0xA0",
2726                   "position": 8,
2727                   "value": 0
2728                 }
2729             )"_json;
2730         parseI2CWriteBit(element);
2731         ADD_FAILURE() << "Should not have reached this line.";
2732     }
2733     catch (const std::invalid_argument& e)
2734     {
2735         EXPECT_STREQ(e.what(), "Element is not a bit position");
2736     }
2737 
2738     // Test where fails: value value is invalid
2739     try
2740     {
2741         const json element = R"(
2742                 {
2743                   "register": "0xA0",
2744                   "position": 3,
2745                   "value": 2
2746                 }
2747             )"_json;
2748         parseI2CWriteBit(element);
2749         ADD_FAILURE() << "Should not have reached this line.";
2750     }
2751     catch (const std::invalid_argument& e)
2752     {
2753         EXPECT_STREQ(e.what(), "Element is not a bit value");
2754     }
2755 
2756     // Test where fails: Required register property not specified
2757     try
2758     {
2759         const json element = R"(
2760             {
2761               "position": 3,
2762               "value": 0
2763             }
2764         )"_json;
2765         parseI2CWriteBit(element);
2766         ADD_FAILURE() << "Should not have reached this line.";
2767     }
2768     catch (const std::invalid_argument& e)
2769     {
2770         EXPECT_STREQ(e.what(), "Required property missing: register");
2771     }
2772 
2773     // Test where fails: Required position property not specified
2774     try
2775     {
2776         const json element = R"(
2777             {
2778               "register": "0xA0",
2779               "value": 0
2780             }
2781         )"_json;
2782         parseI2CWriteBit(element);
2783         ADD_FAILURE() << "Should not have reached this line.";
2784     }
2785     catch (const std::invalid_argument& e)
2786     {
2787         EXPECT_STREQ(e.what(), "Required property missing: position");
2788     }
2789 
2790     // Test where fails: Required value property not specified
2791     try
2792     {
2793         const json element = R"(
2794             {
2795               "register": "0xA0",
2796               "position": 3
2797             }
2798         )"_json;
2799         parseI2CWriteBit(element);
2800         ADD_FAILURE() << "Should not have reached this line.";
2801     }
2802     catch (const std::invalid_argument& e)
2803     {
2804         EXPECT_STREQ(e.what(), "Required property missing: value");
2805     }
2806 }
2807 
2808 TEST(ConfigFileParserTests, ParseI2CWriteByte)
2809 {
2810     // Test where works: Only required properties specified
2811     {
2812         const json element = R"(
2813             {
2814               "register": "0x0A",
2815               "value": "0xCC"
2816             }
2817         )"_json;
2818         std::unique_ptr<I2CWriteByteAction> action = parseI2CWriteByte(element);
2819         EXPECT_EQ(action->getRegister(), 0x0A);
2820         EXPECT_EQ(action->getValue(), 0xCC);
2821         EXPECT_EQ(action->getMask(), 0xFF);
2822     }
2823 
2824     // Test where works: All properties specified
2825     {
2826         const json element = R"(
2827             {
2828               "register": "0x0A",
2829               "value": "0xCC",
2830               "mask": "0xF7"
2831             }
2832         )"_json;
2833         std::unique_ptr<I2CWriteByteAction> action = parseI2CWriteByte(element);
2834         EXPECT_EQ(action->getRegister(), 0x0A);
2835         EXPECT_EQ(action->getValue(), 0xCC);
2836         EXPECT_EQ(action->getMask(), 0xF7);
2837     }
2838 
2839     // Test where fails: Element is not an object
2840     try
2841     {
2842         const json element = R"( [ "0xFF", "0x01" ] )"_json;
2843         parseI2CWriteByte(element);
2844         ADD_FAILURE() << "Should not have reached this line.";
2845     }
2846     catch (const std::invalid_argument& e)
2847     {
2848         EXPECT_STREQ(e.what(), "Element is not an object");
2849     }
2850 
2851     // Test where fails: Invalid property specified
2852     try
2853     {
2854         const json element = R"(
2855             {
2856               "register": "0x0A",
2857               "value": "0xCC",
2858               "mask": "0xF7",
2859               "foo": 1
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 contains an invalid property");
2868     }
2869 
2870     // Test where fails: register value is invalid
2871     try
2872     {
2873         const json element = R"(
2874             {
2875               "register": "0x0Z",
2876               "value": "0xCC",
2877               "mask": "0xF7"
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: value value is invalid
2889     try
2890     {
2891         const json element = R"(
2892             {
2893               "register": "0x0A",
2894               "value": "0xCCC",
2895               "mask": "0xF7"
2896             }
2897         )"_json;
2898         parseI2CWriteByte(element);
2899         ADD_FAILURE() << "Should not have reached this line.";
2900     }
2901     catch (const std::invalid_argument& e)
2902     {
2903         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2904     }
2905 
2906     // Test where fails: mask value is invalid
2907     try
2908     {
2909         const json element = R"(
2910             {
2911               "register": "0x0A",
2912               "value": "0xCC",
2913               "mask": "F7"
2914             }
2915         )"_json;
2916         parseI2CWriteByte(element);
2917         ADD_FAILURE() << "Should not have reached this line.";
2918     }
2919     catch (const std::invalid_argument& e)
2920     {
2921         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
2922     }
2923 
2924     // Test where fails: Required register property not specified
2925     try
2926     {
2927         const json element = R"(
2928             {
2929               "value": "0xCC",
2930               "mask": "0xF7"
2931             }
2932         )"_json;
2933         parseI2CWriteByte(element);
2934         ADD_FAILURE() << "Should not have reached this line.";
2935     }
2936     catch (const std::invalid_argument& e)
2937     {
2938         EXPECT_STREQ(e.what(), "Required property missing: register");
2939     }
2940 
2941     // Test where fails: Required value property not specified
2942     try
2943     {
2944         const json element = R"(
2945             {
2946               "register": "0x0A",
2947               "mask": "0xF7"
2948             }
2949         )"_json;
2950         parseI2CWriteByte(element);
2951         ADD_FAILURE() << "Should not have reached this line.";
2952     }
2953     catch (const std::invalid_argument& e)
2954     {
2955         EXPECT_STREQ(e.what(), "Required property missing: value");
2956     }
2957 }
2958 
2959 TEST(ConfigFileParserTests, ParseI2CWriteBytes)
2960 {
2961     // Test where works: Only required properties specified
2962     {
2963         const json element = R"(
2964             {
2965               "register": "0x0A",
2966               "values": [ "0xCC", "0xFF" ]
2967             }
2968         )"_json;
2969         std::unique_ptr<I2CWriteBytesAction> action =
2970             parseI2CWriteBytes(element);
2971         EXPECT_EQ(action->getRegister(), 0x0A);
2972         EXPECT_EQ(action->getValues().size(), 2);
2973         EXPECT_EQ(action->getValues()[0], 0xCC);
2974         EXPECT_EQ(action->getValues()[1], 0xFF);
2975         EXPECT_EQ(action->getMasks().size(), 0);
2976     }
2977 
2978     // Test where works: All properties specified
2979     {
2980         const json element = R"(
2981             {
2982               "register": "0x0A",
2983               "values": [ "0xCC", "0xFF" ],
2984               "masks":  [ "0x7F", "0x77" ]
2985             }
2986         )"_json;
2987         std::unique_ptr<I2CWriteBytesAction> action =
2988             parseI2CWriteBytes(element);
2989         EXPECT_EQ(action->getRegister(), 0x0A);
2990         EXPECT_EQ(action->getValues().size(), 2);
2991         EXPECT_EQ(action->getValues()[0], 0xCC);
2992         EXPECT_EQ(action->getValues()[1], 0xFF);
2993         EXPECT_EQ(action->getMasks().size(), 2);
2994         EXPECT_EQ(action->getMasks()[0], 0x7F);
2995         EXPECT_EQ(action->getMasks()[1], 0x77);
2996     }
2997 
2998     // Test where fails: Element is not an object
2999     try
3000     {
3001         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3002         parseI2CWriteBytes(element);
3003         ADD_FAILURE() << "Should not have reached this line.";
3004     }
3005     catch (const std::invalid_argument& e)
3006     {
3007         EXPECT_STREQ(e.what(), "Element is not an object");
3008     }
3009 
3010     // Test where fails: Invalid property specified
3011     try
3012     {
3013         const json element = R"(
3014             {
3015               "register": "0x0A",
3016               "values": [ "0xCC", "0xFF" ],
3017               "masks":  [ "0x7F", "0x7F" ],
3018               "foo": 1
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 contains an invalid property");
3027     }
3028 
3029     // Test where fails: register value is invalid
3030     try
3031     {
3032         const json element = R"(
3033             {
3034               "register": "0x0Z",
3035               "values": [ "0xCC", "0xFF" ],
3036               "masks":  [ "0x7F", "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: values value is invalid
3048     try
3049     {
3050         const json element = R"(
3051             {
3052               "register": "0x0A",
3053               "values": [ "0xCCC", "0xFF" ],
3054               "masks":  [ "0x7F", "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(), "Element is not hexadecimal string");
3063     }
3064 
3065     // Test where fails: masks value is invalid
3066     try
3067     {
3068         const json element = R"(
3069             {
3070               "register": "0x0A",
3071               "values": [ "0xCC", "0xFF" ],
3072               "masks":  [ "F", "0x7F" ]
3073             }
3074         )"_json;
3075         parseI2CWriteBytes(element);
3076         ADD_FAILURE() << "Should not have reached this line.";
3077     }
3078     catch (const std::invalid_argument& e)
3079     {
3080         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
3081     }
3082 
3083     // Test where fails: number of elements in masks is invalid
3084     try
3085     {
3086         const json element = R"(
3087             {
3088               "register": "0x0A",
3089               "values": [ "0xCC", "0xFF" ],
3090               "masks":  [ "0x7F" ]
3091             }
3092         )"_json;
3093         parseI2CWriteBytes(element);
3094         ADD_FAILURE() << "Should not have reached this line.";
3095     }
3096     catch (const std::invalid_argument& e)
3097     {
3098         EXPECT_STREQ(e.what(), "Invalid number of elements in masks");
3099     }
3100 
3101     // Test where fails: Required register property not specified
3102     try
3103     {
3104         const json element = R"(
3105             {
3106               "values": [ "0xCC", "0xFF" ]
3107             }
3108         )"_json;
3109         parseI2CWriteBytes(element);
3110         ADD_FAILURE() << "Should not have reached this line.";
3111     }
3112     catch (const std::invalid_argument& e)
3113     {
3114         EXPECT_STREQ(e.what(), "Required property missing: register");
3115     }
3116 
3117     // Test where fails: Required values property not specified
3118     try
3119     {
3120         const json element = R"(
3121             {
3122               "register": "0x0A"
3123             }
3124         )"_json;
3125         parseI2CWriteBytes(element);
3126         ADD_FAILURE() << "Should not have reached this line.";
3127     }
3128     catch (const std::invalid_argument& e)
3129     {
3130         EXPECT_STREQ(e.what(), "Required property missing: values");
3131     }
3132 }
3133 
3134 TEST(ConfigFileParserTests, ParseIf)
3135 {
3136     // Test where works: Only required properties specified
3137     {
3138         const json element = R"(
3139             {
3140               "condition": { "run_rule": "is_downlevel_regulator" },
3141               "then": [ { "run_rule": "configure_downlevel_regulator" },
3142                         { "run_rule": "configure_standard_regulator" } ]
3143             }
3144         )"_json;
3145         std::unique_ptr<IfAction> action = parseIf(element);
3146         EXPECT_NE(action->getConditionAction().get(), nullptr);
3147         EXPECT_EQ(action->getThenActions().size(), 2);
3148         EXPECT_EQ(action->getElseActions().size(), 0);
3149     }
3150 
3151     // Test where works: All properties specified
3152     {
3153         const json element = R"(
3154             {
3155               "condition": { "run_rule": "is_downlevel_regulator" },
3156               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3157               "else": [ { "run_rule": "configure_standard_regulator" } ]
3158             }
3159         )"_json;
3160         std::unique_ptr<IfAction> action = parseIf(element);
3161         EXPECT_NE(action->getConditionAction().get(), nullptr);
3162         EXPECT_EQ(action->getThenActions().size(), 1);
3163         EXPECT_EQ(action->getElseActions().size(), 1);
3164     }
3165 
3166     // Test where fails: Required condition property not specified
3167     try
3168     {
3169         const json element = R"(
3170             {
3171               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3172               "else": [ { "run_rule": "configure_standard_regulator" } ]
3173             }
3174         )"_json;
3175         parseIf(element);
3176         ADD_FAILURE() << "Should not have reached this line.";
3177     }
3178     catch (const std::invalid_argument& e)
3179     {
3180         EXPECT_STREQ(e.what(), "Required property missing: condition");
3181     }
3182 
3183     // Test where fails: Required then property not specified
3184     try
3185     {
3186         const json element = R"(
3187             {
3188               "condition": { "run_rule": "is_downlevel_regulator" },
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(), "Required property missing: then");
3198     }
3199 
3200     // Test where fails: condition value is invalid
3201     try
3202     {
3203         const json element = R"(
3204             {
3205               "condition": 1,
3206               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3207               "else": [ { "run_rule": "configure_standard_regulator" } ]
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 object");
3216     }
3217 
3218     // Test where fails: then value is invalid
3219     try
3220     {
3221         const json element = R"(
3222             {
3223               "condition": { "run_rule": "is_downlevel_regulator" },
3224               "then": "foo",
3225               "else": [ { "run_rule": "configure_standard_regulator" } ]
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 is not an array");
3234     }
3235 
3236     // Test where fails: else value is invalid
3237     try
3238     {
3239         const json element = R"(
3240             {
3241               "condition": { "run_rule": "is_downlevel_regulator" },
3242               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3243               "else": 1
3244             }
3245         )"_json;
3246         parseIf(element);
3247         ADD_FAILURE() << "Should not have reached this line.";
3248     }
3249     catch (const std::invalid_argument& e)
3250     {
3251         EXPECT_STREQ(e.what(), "Element is not an array");
3252     }
3253 
3254     // Test where fails: Invalid property specified
3255     try
3256     {
3257         const json element = R"(
3258             {
3259               "condition": { "run_rule": "is_downlevel_regulator" },
3260               "then": [ { "run_rule": "configure_downlevel_regulator" } ],
3261               "foo": "bar"
3262             }
3263         )"_json;
3264         parseIf(element);
3265         ADD_FAILURE() << "Should not have reached this line.";
3266     }
3267     catch (const std::invalid_argument& e)
3268     {
3269         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3270     }
3271 
3272     // Test where fails: Element is not an object
3273     try
3274     {
3275         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3276         parseIf(element);
3277         ADD_FAILURE() << "Should not have reached this line.";
3278     }
3279     catch (const std::invalid_argument& e)
3280     {
3281         EXPECT_STREQ(e.what(), "Element is not an object");
3282     }
3283 }
3284 
3285 TEST(ConfigFileParserTests, ParseInt8)
3286 {
3287     // Test where works: INT8_MIN
3288     {
3289         const json element = R"( -128 )"_json;
3290         int8_t value = parseInt8(element);
3291         EXPECT_EQ(value, -128);
3292     }
3293 
3294     // Test where works: INT8_MAX
3295     {
3296         const json element = R"( 127 )"_json;
3297         int8_t value = parseInt8(element);
3298         EXPECT_EQ(value, 127);
3299     }
3300 
3301     // Test where fails: Element is not an integer
3302     try
3303     {
3304         const json element = R"( 1.03 )"_json;
3305         parseInt8(element);
3306         ADD_FAILURE() << "Should not have reached this line.";
3307     }
3308     catch (const std::invalid_argument& e)
3309     {
3310         EXPECT_STREQ(e.what(), "Element is not an integer");
3311     }
3312 
3313     // Test where fails: Value < INT8_MIN
3314     try
3315     {
3316         const json element = R"( -129 )"_json;
3317         parseInt8(element);
3318         ADD_FAILURE() << "Should not have reached this line.";
3319     }
3320     catch (const std::invalid_argument& e)
3321     {
3322         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
3323     }
3324 
3325     // Test where fails: Value > INT8_MAX
3326     try
3327     {
3328         const json element = R"( 128 )"_json;
3329         parseInt8(element);
3330         ADD_FAILURE() << "Should not have reached this line.";
3331     }
3332     catch (const std::invalid_argument& e)
3333     {
3334         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
3335     }
3336 }
3337 
3338 TEST(ConfigFileParserTests, ParseInventoryPath)
3339 {
3340     // Test where works: Inventory path has a leading '/'
3341     {
3342         const json element = "/system/chassis/motherboard/cpu3";
3343         std::string value = parseInventoryPath(element);
3344         EXPECT_EQ(
3345             value,
3346             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu3");
3347     }
3348 
3349     // Test where works: Inventory path does not have a leading '/'
3350     {
3351         const json element = "system/chassis/motherboard/cpu1";
3352         std::string value = parseInventoryPath(element);
3353         EXPECT_EQ(
3354             value,
3355             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1");
3356     }
3357 
3358     // Test where fails: JSON element is not a string
3359     try
3360     {
3361         const json element = R"( { "foo": "bar" } )"_json;
3362         parseInventoryPath(element);
3363         ADD_FAILURE() << "Should not have reached this line.";
3364     }
3365     catch (const std::invalid_argument& e)
3366     {
3367         EXPECT_STREQ(e.what(), "Element is not a string");
3368     }
3369 
3370     // Test where fails: JSON element contains an empty string
3371     try
3372     {
3373         const json element = "";
3374         parseInventoryPath(element);
3375         ADD_FAILURE() << "Should not have reached this line.";
3376     }
3377     catch (const std::invalid_argument& e)
3378     {
3379         EXPECT_STREQ(e.what(), "Element contains an empty string");
3380     }
3381 }
3382 
3383 TEST(ConfigFileParserTests, ParseLogPhaseFault)
3384 {
3385     // Test where works
3386     {
3387         const json element = R"(
3388             {
3389               "type": "n+1"
3390             }
3391         )"_json;
3392         std::unique_ptr<LogPhaseFaultAction> action =
3393             parseLogPhaseFault(element);
3394         EXPECT_EQ(action->getType(), PhaseFaultType::n_plus_1);
3395     }
3396 
3397     // Test where fails: Element is not an object
3398     try
3399     {
3400         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3401         parseLogPhaseFault(element);
3402         ADD_FAILURE() << "Should not have reached this line.";
3403     }
3404     catch (const std::invalid_argument& e)
3405     {
3406         EXPECT_STREQ(e.what(), "Element is not an object");
3407     }
3408 
3409     // Test where fails: Required type property not specified
3410     try
3411     {
3412         const json element = R"(
3413             {
3414             }
3415         )"_json;
3416         parseLogPhaseFault(element);
3417         ADD_FAILURE() << "Should not have reached this line.";
3418     }
3419     catch (const std::invalid_argument& e)
3420     {
3421         EXPECT_STREQ(e.what(), "Required property missing: type");
3422     }
3423 
3424     // Test where fails: type value is invalid
3425     try
3426     {
3427         const json element = R"(
3428             {
3429               "type": "n+2"
3430             }
3431         )"_json;
3432         parseLogPhaseFault(element);
3433         ADD_FAILURE() << "Should not have reached this line.";
3434     }
3435     catch (const std::invalid_argument& e)
3436     {
3437         EXPECT_STREQ(e.what(), "Element is not a phase fault type");
3438     }
3439 
3440     // Test where fails: Invalid property specified
3441     try
3442     {
3443         const json element = R"(
3444             {
3445               "type": "n+1",
3446               "foo": 1
3447             }
3448         )"_json;
3449         parseLogPhaseFault(element);
3450         ADD_FAILURE() << "Should not have reached this line.";
3451     }
3452     catch (const std::invalid_argument& e)
3453     {
3454         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3455     }
3456 }
3457 
3458 TEST(ConfigFileParserTests, ParseNot)
3459 {
3460     // Test where works
3461     {
3462         const json element = R"(
3463             { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }
3464         )"_json;
3465         std::unique_ptr<NotAction> action = parseNot(element);
3466         EXPECT_NE(action->getAction().get(), nullptr);
3467     }
3468 
3469     // Test where fails: Element is not an object
3470     try
3471     {
3472         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3473         parseNot(element);
3474         ADD_FAILURE() << "Should not have reached this line.";
3475     }
3476     catch (const std::invalid_argument& e)
3477     {
3478         EXPECT_STREQ(e.what(), "Element is not an object");
3479     }
3480 }
3481 
3482 TEST(ConfigFileParserTests, ParseOr)
3483 {
3484     // Test where works: Element is an array with 2 actions
3485     {
3486         const json element = R"(
3487             [
3488               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
3489               { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
3490             ]
3491         )"_json;
3492         std::unique_ptr<OrAction> action = parseOr(element);
3493         EXPECT_EQ(action->getActions().size(), 2);
3494     }
3495 
3496     // Test where fails: Element is an array with 1 action
3497     try
3498     {
3499         const json element = R"(
3500             [
3501               { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }
3502             ]
3503         )"_json;
3504         parseOr(element);
3505         ADD_FAILURE() << "Should not have reached this line.";
3506     }
3507     catch (const std::invalid_argument& e)
3508     {
3509         EXPECT_STREQ(e.what(), "Array must contain two or more actions");
3510     }
3511 
3512     // Test where fails: Element is not an array
3513     try
3514     {
3515         const json element = R"(
3516             {
3517               "foo": "bar"
3518             }
3519         )"_json;
3520         parseOr(element);
3521         ADD_FAILURE() << "Should not have reached this line.";
3522     }
3523     catch (const std::invalid_argument& e)
3524     {
3525         EXPECT_STREQ(e.what(), "Element is not an array");
3526     }
3527 }
3528 
3529 TEST(ConfigFileParserTests, ParsePhaseFaultDetection)
3530 {
3531     // Test where works: actions specified: optional properties not specified
3532     {
3533         const json element = R"(
3534             {
3535               "actions": [
3536                 { "run_rule": "detect_phase_fault_rule" }
3537               ]
3538             }
3539         )"_json;
3540         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
3541             parsePhaseFaultDetection(element);
3542         EXPECT_EQ(phaseFaultDetection->getActions().size(), 1);
3543         EXPECT_EQ(phaseFaultDetection->getDeviceID(), "");
3544     }
3545 
3546     // Test where works: rule_id specified: optional properties specified
3547     {
3548         const json element = R"(
3549             {
3550               "comments": [ "Detect phase fault using I/O expander" ],
3551               "device_id": "io_expander",
3552               "rule_id": "detect_phase_fault_rule"
3553             }
3554         )"_json;
3555         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
3556             parsePhaseFaultDetection(element);
3557         EXPECT_EQ(phaseFaultDetection->getActions().size(), 1);
3558         EXPECT_EQ(phaseFaultDetection->getDeviceID(), "io_expander");
3559     }
3560 
3561     // Test where fails: Element is not an object
3562     try
3563     {
3564         const json element = R"( [ "foo", "bar" ] )"_json;
3565         parsePhaseFaultDetection(element);
3566         ADD_FAILURE() << "Should not have reached this line.";
3567     }
3568     catch (const std::invalid_argument& e)
3569     {
3570         EXPECT_STREQ(e.what(), "Element is not an object");
3571     }
3572 
3573     // Test where fails: device_id value is invalid
3574     try
3575     {
3576         const json element = R"(
3577             {
3578               "device_id": 1,
3579               "rule_id": "detect_phase_fault_rule"
3580             }
3581         )"_json;
3582         parsePhaseFaultDetection(element);
3583         ADD_FAILURE() << "Should not have reached this line.";
3584     }
3585     catch (const std::invalid_argument& e)
3586     {
3587         EXPECT_STREQ(e.what(), "Element is not a string");
3588     }
3589 
3590     // Test where fails: rule_id value is invalid
3591     try
3592     {
3593         const json element = R"(
3594             {
3595               "rule_id": 1
3596             }
3597         )"_json;
3598         parsePhaseFaultDetection(element);
3599         ADD_FAILURE() << "Should not have reached this line.";
3600     }
3601     catch (const std::invalid_argument& e)
3602     {
3603         EXPECT_STREQ(e.what(), "Element is not a string");
3604     }
3605 
3606     // Test where fails: actions object is invalid
3607     try
3608     {
3609         const json element = R"(
3610             {
3611               "actions": 1
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(), "Element is not an array");
3620     }
3621 
3622     // Test where fails: Required actions or rule_id property not specified
3623     try
3624     {
3625         const json element = R"(
3626             {
3627               "device_id": "io_expander"
3628             }
3629         )"_json;
3630         parsePhaseFaultDetection(element);
3631         ADD_FAILURE() << "Should not have reached this line.";
3632     }
3633     catch (const std::invalid_argument& e)
3634     {
3635         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
3636                                "either rule_id or actions");
3637     }
3638 
3639     // Test where fails: Required actions or rule_id property both specified
3640     try
3641     {
3642         const json element = R"(
3643             {
3644               "rule_id": "detect_phase_fault_rule",
3645               "actions": [
3646                 { "run_rule": "detect_phase_fault_rule" }
3647               ]
3648             }
3649         )"_json;
3650         parsePhaseFaultDetection(element);
3651         ADD_FAILURE() << "Should not have reached this line.";
3652     }
3653     catch (const std::invalid_argument& e)
3654     {
3655         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
3656                                "either rule_id or actions");
3657     }
3658 
3659     // Test where fails: Invalid property specified
3660     try
3661     {
3662         const json element = R"(
3663             {
3664               "foo": "bar",
3665               "actions": [
3666                 { "run_rule": "detect_phase_fault_rule" }
3667               ]
3668             }
3669         )"_json;
3670         parsePhaseFaultDetection(element);
3671         ADD_FAILURE() << "Should not have reached this line.";
3672     }
3673     catch (const std::invalid_argument& e)
3674     {
3675         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3676     }
3677 }
3678 
3679 TEST(ConfigFileParserTests, ParsePhaseFaultType)
3680 {
3681     // Test where works: n
3682     {
3683         const json element = "n";
3684         PhaseFaultType type = parsePhaseFaultType(element);
3685         EXPECT_EQ(type, PhaseFaultType::n);
3686     }
3687 
3688     // Test where works: n+1
3689     {
3690         const json element = "n+1";
3691         PhaseFaultType type = parsePhaseFaultType(element);
3692         EXPECT_EQ(type, PhaseFaultType::n_plus_1);
3693     }
3694 
3695     // Test where fails: Element is not a phase fault type
3696     try
3697     {
3698         const json element = "n+2";
3699         parsePhaseFaultType(element);
3700         ADD_FAILURE() << "Should not have reached this line.";
3701     }
3702     catch (const std::invalid_argument& e)
3703     {
3704         EXPECT_STREQ(e.what(), "Element is not a phase fault type");
3705     }
3706 
3707     // Test where fails: Element is not a string
3708     try
3709     {
3710         const json element = R"( { "foo": "bar" } )"_json;
3711         parsePhaseFaultType(element);
3712         ADD_FAILURE() << "Should not have reached this line.";
3713     }
3714     catch (const std::invalid_argument& e)
3715     {
3716         EXPECT_STREQ(e.what(), "Element is not a string");
3717     }
3718 }
3719 
3720 TEST(ConfigFileParserTests, ParsePMBusReadSensor)
3721 {
3722     // Test where works: Only required properties specified
3723     {
3724         const json element = R"(
3725             {
3726               "type": "iout",
3727               "command": "0x8C",
3728               "format": "linear_11"
3729             }
3730         )"_json;
3731         std::unique_ptr<PMBusReadSensorAction> action =
3732             parsePMBusReadSensor(element);
3733         EXPECT_EQ(action->getType(), SensorType::iout);
3734         EXPECT_EQ(action->getCommand(), 0x8C);
3735         EXPECT_EQ(action->getFormat(),
3736                   pmbus_utils::SensorDataFormat::linear_11);
3737         EXPECT_EQ(action->getExponent().has_value(), false);
3738     }
3739 
3740     // Test where works: All properties specified
3741     {
3742         const json element = R"(
3743             {
3744               "type": "temperature",
3745               "command": "0x7A",
3746               "format": "linear_16",
3747               "exponent": -8
3748             }
3749         )"_json;
3750         std::unique_ptr<PMBusReadSensorAction> action =
3751             parsePMBusReadSensor(element);
3752         EXPECT_EQ(action->getType(), SensorType::temperature);
3753         EXPECT_EQ(action->getCommand(), 0x7A);
3754         EXPECT_EQ(action->getFormat(),
3755                   pmbus_utils::SensorDataFormat::linear_16);
3756         EXPECT_EQ(action->getExponent().has_value(), true);
3757         EXPECT_EQ(action->getExponent().value(), -8);
3758     }
3759 
3760     // Test where fails: Element is not an object
3761     try
3762     {
3763         const json element = R"( [ "0xFF", "0x01" ] )"_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(), "Element is not an object");
3770     }
3771 
3772     // Test where fails: Invalid property specified
3773     try
3774     {
3775         const json element = R"(
3776             {
3777               "type": "iout",
3778               "command": "0x8C",
3779               "format": "linear_11",
3780               "foo": 1
3781             }
3782         )"_json;
3783         parsePMBusReadSensor(element);
3784         ADD_FAILURE() << "Should not have reached this line.";
3785     }
3786     catch (const std::invalid_argument& e)
3787     {
3788         EXPECT_STREQ(e.what(), "Element contains an invalid property");
3789     }
3790 
3791     // Test where fails: Required type property not specified
3792     try
3793     {
3794         const json element = R"(
3795             {
3796               "command": "0x8C",
3797               "format": "linear_11"
3798             }
3799         )"_json;
3800         parsePMBusReadSensor(element);
3801         ADD_FAILURE() << "Should not have reached this line.";
3802     }
3803     catch (const std::invalid_argument& e)
3804     {
3805         EXPECT_STREQ(e.what(), "Required property missing: type");
3806     }
3807 
3808     // Test where fails: Required command property not specified
3809     try
3810     {
3811         const json element = R"(
3812             {
3813               "type": "iout",
3814               "format": "linear_11"
3815             }
3816         )"_json;
3817         parsePMBusReadSensor(element);
3818         ADD_FAILURE() << "Should not have reached this line.";
3819     }
3820     catch (const std::invalid_argument& e)
3821     {
3822         EXPECT_STREQ(e.what(), "Required property missing: command");
3823     }
3824 
3825     // Test where fails: Required format property not specified
3826     try
3827     {
3828         const json element = R"(
3829             {
3830               "type": "iout",
3831               "command": "0x8C"
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(), "Required property missing: format");
3840     }
3841 
3842     // Test where fails: type value is invalid
3843     try
3844     {
3845         const json element = R"(
3846             {
3847               "type": 1,
3848               "command": "0x7A",
3849               "format": "linear_16"
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: command value is invalid
3861     try
3862     {
3863         const json element = R"(
3864             {
3865               "type": "temperature",
3866               "command": 0,
3867               "format": "linear_16"
3868             }
3869         )"_json;
3870         parsePMBusReadSensor(element);
3871         ADD_FAILURE() << "Should not have reached this line.";
3872     }
3873     catch (const std::invalid_argument& e)
3874     {
3875         EXPECT_STREQ(e.what(), "Element is not a string");
3876     }
3877 
3878     // Test where fails: format value is invalid
3879     try
3880     {
3881         const json element = R"(
3882             {
3883               "type": "temperature",
3884               "command": "0x7A",
3885               "format": 1
3886             }
3887         )"_json;
3888         parsePMBusReadSensor(element);
3889         ADD_FAILURE() << "Should not have reached this line.";
3890     }
3891     catch (const std::invalid_argument& e)
3892     {
3893         EXPECT_STREQ(e.what(), "Element is not a string");
3894     }
3895 
3896     // Test where fails: exponent value is invalid
3897     try
3898     {
3899         const json element = R"(
3900             {
3901               "type": "temperature",
3902               "command": "0x7A",
3903               "format": "linear_16",
3904               "exponent": 1.3
3905             }
3906         )"_json;
3907         parsePMBusReadSensor(element);
3908         ADD_FAILURE() << "Should not have reached this line.";
3909     }
3910     catch (const std::invalid_argument& e)
3911     {
3912         EXPECT_STREQ(e.what(), "Element is not an integer");
3913     }
3914 }
3915 
3916 TEST(ConfigFileParserTests, ParsePMBusWriteVoutCommand)
3917 {
3918     // Test where works: Only required properties specified
3919     {
3920         const json element = R"(
3921             {
3922               "format": "linear"
3923             }
3924         )"_json;
3925         std::unique_ptr<PMBusWriteVoutCommandAction> action =
3926             parsePMBusWriteVoutCommand(element);
3927         EXPECT_EQ(action->getVolts().has_value(), false);
3928         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
3929         EXPECT_EQ(action->getExponent().has_value(), false);
3930         EXPECT_EQ(action->isVerified(), false);
3931     }
3932 
3933     // Test where works: All properties specified
3934     {
3935         const json element = R"(
3936             {
3937               "volts": 1.03,
3938               "format": "linear",
3939               "exponent": -8,
3940               "is_verified": true
3941             }
3942         )"_json;
3943         std::unique_ptr<PMBusWriteVoutCommandAction> action =
3944             parsePMBusWriteVoutCommand(element);
3945         EXPECT_EQ(action->getVolts().has_value(), true);
3946         EXPECT_EQ(action->getVolts().value(), 1.03);
3947         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
3948         EXPECT_EQ(action->getExponent().has_value(), true);
3949         EXPECT_EQ(action->getExponent().value(), -8);
3950         EXPECT_EQ(action->isVerified(), true);
3951     }
3952 
3953     // Test where fails: Element is not an object
3954     try
3955     {
3956         const json element = R"( [ "0xFF", "0x01" ] )"_json;
3957         parsePMBusWriteVoutCommand(element);
3958         ADD_FAILURE() << "Should not have reached this line.";
3959     }
3960     catch (const std::invalid_argument& e)
3961     {
3962         EXPECT_STREQ(e.what(), "Element is not an object");
3963     }
3964 
3965     // Test where fails: volts value is invalid
3966     try
3967     {
3968         const json element = R"(
3969             {
3970               "volts": "foo",
3971               "format": "linear"
3972             }
3973         )"_json;
3974         parsePMBusWriteVoutCommand(element);
3975         ADD_FAILURE() << "Should not have reached this line.";
3976     }
3977     catch (const std::invalid_argument& e)
3978     {
3979         EXPECT_STREQ(e.what(), "Element is not a number");
3980     }
3981 
3982     // Test where fails: Required format property not specified
3983     try
3984     {
3985         const json element = R"(
3986             {
3987               "volts": 1.03,
3988               "is_verified": true
3989             }
3990         )"_json;
3991         parsePMBusWriteVoutCommand(element);
3992         ADD_FAILURE() << "Should not have reached this line.";
3993     }
3994     catch (const std::invalid_argument& e)
3995     {
3996         EXPECT_STREQ(e.what(), "Required property missing: format");
3997     }
3998 
3999     // Test where fails: format value is invalid
4000     try
4001     {
4002         const json element = R"(
4003             {
4004               "format": "linear_11"
4005             }
4006         )"_json;
4007         parsePMBusWriteVoutCommand(element);
4008         ADD_FAILURE() << "Should not have reached this line.";
4009     }
4010     catch (const std::invalid_argument& e)
4011     {
4012         EXPECT_STREQ(e.what(), "Invalid format value: linear_11");
4013     }
4014 
4015     // Test where fails: exponent value is invalid
4016     try
4017     {
4018         const json element = R"(
4019             {
4020               "format": "linear",
4021               "exponent": 1.3
4022             }
4023         )"_json;
4024         parsePMBusWriteVoutCommand(element);
4025         ADD_FAILURE() << "Should not have reached this line.";
4026     }
4027     catch (const std::invalid_argument& e)
4028     {
4029         EXPECT_STREQ(e.what(), "Element is not an integer");
4030     }
4031 
4032     // Test where fails: is_verified value is invalid
4033     try
4034     {
4035         const json element = R"(
4036             {
4037               "format": "linear",
4038               "is_verified": "true"
4039             }
4040         )"_json;
4041         parsePMBusWriteVoutCommand(element);
4042         ADD_FAILURE() << "Should not have reached this line.";
4043     }
4044     catch (const std::invalid_argument& e)
4045     {
4046         EXPECT_STREQ(e.what(), "Element is not a boolean");
4047     }
4048 
4049     // Test where fails: Invalid property specified
4050     try
4051     {
4052         const json element = R"(
4053             {
4054               "format": "linear",
4055               "foo": "bar"
4056             }
4057         )"_json;
4058         parsePMBusWriteVoutCommand(element);
4059         ADD_FAILURE() << "Should not have reached this line.";
4060     }
4061     catch (const std::invalid_argument& e)
4062     {
4063         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4064     }
4065 }
4066 
4067 TEST(ConfigFileParserTests, ParsePresenceDetection)
4068 {
4069     // Test where works: actions property specified
4070     {
4071         const json element = R"(
4072             {
4073               "actions": [
4074                 { "run_rule": "read_sensors_rule" }
4075               ]
4076             }
4077         )"_json;
4078         std::unique_ptr<PresenceDetection> presenceDetection =
4079             parsePresenceDetection(element);
4080         EXPECT_EQ(presenceDetection->getActions().size(), 1);
4081     }
4082 
4083     // Test where works: rule_id property specified
4084     {
4085         const json element = R"(
4086             {
4087               "comments": [ "comments property" ],
4088               "rule_id": "set_voltage_rule"
4089             }
4090         )"_json;
4091         std::unique_ptr<PresenceDetection> presenceDetection =
4092             parsePresenceDetection(element);
4093         EXPECT_EQ(presenceDetection->getActions().size(), 1);
4094     }
4095 
4096     // Test where fails: actions object is invalid
4097     try
4098     {
4099         const json element = R"(
4100             {
4101               "actions": 1
4102             }
4103         )"_json;
4104         parsePresenceDetection(element);
4105         ADD_FAILURE() << "Should not have reached this line.";
4106     }
4107     catch (const std::invalid_argument& e)
4108     {
4109         EXPECT_STREQ(e.what(), "Element is not an array");
4110     }
4111 
4112     // Test where fails: rule_id value is invalid
4113     try
4114     {
4115         const json element = R"(
4116             {
4117               "rule_id": 1
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(), "Element is not a string");
4126     }
4127 
4128     // Test where fails: Required actions or rule_id property not specified
4129     try
4130     {
4131         const json element = R"(
4132             {
4133               "comments": [ "comments property" ]
4134             }
4135         )"_json;
4136         parsePresenceDetection(element);
4137         ADD_FAILURE() << "Should not have reached this line.";
4138     }
4139     catch (const std::invalid_argument& e)
4140     {
4141         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4142                                "either rule_id or actions");
4143     }
4144 
4145     // Test where fails: Required actions or rule_id property both specified
4146     try
4147     {
4148         const json element = R"(
4149             {
4150               "rule_id": "set_voltage_rule",
4151               "actions": [
4152                 { "run_rule": "read_sensors_rule" }
4153               ]
4154             }
4155         )"_json;
4156         parsePresenceDetection(element);
4157         ADD_FAILURE() << "Should not have reached this line.";
4158     }
4159     catch (const std::invalid_argument& e)
4160     {
4161         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4162                                "either rule_id or actions");
4163     }
4164 
4165     // Test where fails: Element is not an object
4166     try
4167     {
4168         const json element = R"( [ "foo", "bar" ] )"_json;
4169         parsePresenceDetection(element);
4170         ADD_FAILURE() << "Should not have reached this line.";
4171     }
4172     catch (const std::invalid_argument& e)
4173     {
4174         EXPECT_STREQ(e.what(), "Element is not an object");
4175     }
4176 
4177     // Test where fails: Invalid property specified
4178     try
4179     {
4180         const json element = R"(
4181             {
4182               "foo": "bar",
4183               "actions": [
4184                 { "run_rule": "read_sensors_rule" }
4185               ]
4186             }
4187         )"_json;
4188         parsePresenceDetection(element);
4189         ADD_FAILURE() << "Should not have reached this line.";
4190     }
4191     catch (const std::invalid_argument& e)
4192     {
4193         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4194     }
4195 }
4196 
4197 TEST(ConfigFileParserTests, ParseRail)
4198 {
4199     // Test where works: Only required properties specified
4200     {
4201         const json element = R"(
4202             {
4203               "id": "vdd"
4204             }
4205         )"_json;
4206         std::unique_ptr<Rail> rail = parseRail(element);
4207         EXPECT_EQ(rail->getID(), "vdd");
4208         EXPECT_EQ(rail->getConfiguration(), nullptr);
4209         EXPECT_EQ(rail->getSensorMonitoring(), nullptr);
4210     }
4211 
4212     // Test where works: All properties specified
4213     {
4214         const json element = R"(
4215             {
4216               "comments": [ "comments property" ],
4217               "id": "vdd",
4218               "configuration": {
4219                 "volts": 1.1,
4220                 "actions": [
4221                   {
4222                     "pmbus_write_vout_command": {
4223                       "format": "linear"
4224                     }
4225                   }
4226                 ]
4227               },
4228               "sensor_monitoring": {
4229                 "actions": [
4230                   { "run_rule": "read_sensors_rule" }
4231                 ]
4232               }
4233             }
4234         )"_json;
4235         std::unique_ptr<Rail> rail = parseRail(element);
4236         EXPECT_EQ(rail->getID(), "vdd");
4237         EXPECT_NE(rail->getConfiguration(), nullptr);
4238         EXPECT_NE(rail->getSensorMonitoring(), nullptr);
4239     }
4240 
4241     // Test where fails: id property not specified
4242     try
4243     {
4244         const json element = R"(
4245             {
4246               "configuration": {
4247                 "volts": 1.1,
4248                 "actions": [
4249                   {
4250                     "pmbus_write_vout_command": {
4251                       "format": "linear"
4252                     }
4253                   }
4254                 ]
4255               }
4256             }
4257         )"_json;
4258         parseRail(element);
4259         ADD_FAILURE() << "Should not have reached this line.";
4260     }
4261     catch (const std::invalid_argument& e)
4262     {
4263         EXPECT_STREQ(e.what(), "Required property missing: id");
4264     }
4265 
4266     // Test where fails: id property is invalid
4267     try
4268     {
4269         const json element = R"(
4270             {
4271               "id": "",
4272               "configuration": {
4273                 "volts": 1.1,
4274                 "actions": [
4275                   {
4276                     "pmbus_write_vout_command": {
4277                       "format": "linear"
4278                     }
4279                   }
4280                 ]
4281               }
4282             }
4283         )"_json;
4284         parseRail(element);
4285         ADD_FAILURE() << "Should not have reached this line.";
4286     }
4287     catch (const std::invalid_argument& e)
4288     {
4289         EXPECT_STREQ(e.what(), "Element contains an empty string");
4290     }
4291 
4292     // Test where fails: Element is not an object
4293     try
4294     {
4295         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4296         parseRail(element);
4297         ADD_FAILURE() << "Should not have reached this line.";
4298     }
4299     catch (const std::invalid_argument& e)
4300     {
4301         EXPECT_STREQ(e.what(), "Element is not an object");
4302     }
4303 
4304     // Test where fails: configuration value is invalid
4305     try
4306     {
4307         const json element = R"(
4308             {
4309               "id": "vdd",
4310               "configuration": "config"
4311             }
4312         )"_json;
4313         parseRail(element);
4314         ADD_FAILURE() << "Should not have reached this line.";
4315     }
4316     catch (const std::invalid_argument& e)
4317     {
4318         EXPECT_STREQ(e.what(), "Element is not an object");
4319     }
4320 
4321     // Test where fails: sensor_monitoring value is invalid
4322     try
4323     {
4324         const json element = R"(
4325             {
4326               "comments": [ "comments property" ],
4327               "id": "vdd",
4328               "configuration": {
4329                 "volts": 1.1,
4330                 "actions": [
4331                   {
4332                     "pmbus_write_vout_command": {
4333                       "format": "linear"
4334                     }
4335                   }
4336                 ]
4337               },
4338               "sensor_monitoring": 1
4339             }
4340         )"_json;
4341         parseRail(element);
4342         ADD_FAILURE() << "Should not have reached this line.";
4343     }
4344     catch (const std::invalid_argument& e)
4345     {
4346         EXPECT_STREQ(e.what(), "Element is not an object");
4347     }
4348 
4349     // Test where fails: Invalid property specified
4350     try
4351     {
4352         const json element = R"(
4353             {
4354               "id": "vdd",
4355               "foo" : true
4356             }
4357         )"_json;
4358         parseRail(element);
4359         ADD_FAILURE() << "Should not have reached this line.";
4360     }
4361     catch (const std::invalid_argument& e)
4362     {
4363         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4364     }
4365 }
4366 
4367 TEST(ConfigFileParserTests, ParseRailArray)
4368 {
4369     // Test where works
4370     {
4371         const json element = R"(
4372             [
4373               { "id": "vdd" },
4374               { "id": "vio" }
4375             ]
4376         )"_json;
4377         std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
4378         EXPECT_EQ(rails.size(), 2);
4379         EXPECT_EQ(rails[0]->getID(), "vdd");
4380         EXPECT_EQ(rails[1]->getID(), "vio");
4381     }
4382 
4383     // Test where fails: Element is not an array
4384     try
4385     {
4386         const json element = R"(
4387             {
4388               "foo": "bar"
4389             }
4390         )"_json;
4391         parseRailArray(element);
4392         ADD_FAILURE() << "Should not have reached this line.";
4393     }
4394     catch (const std::invalid_argument& e)
4395     {
4396         EXPECT_STREQ(e.what(), "Element is not an array");
4397     }
4398 }
4399 
4400 TEST(ConfigFileParserTests, ParseRoot)
4401 {
4402     // Test where works: Only required properties specified
4403     {
4404         const json element = R"(
4405             {
4406               "chassis": [
4407                 { "number": 1 }
4408               ]
4409             }
4410         )"_json;
4411         std::vector<std::unique_ptr<Rule>> rules{};
4412         std::vector<std::unique_ptr<Chassis>> chassis{};
4413         std::tie(rules, chassis) = parseRoot(element);
4414         EXPECT_EQ(rules.size(), 0);
4415         EXPECT_EQ(chassis.size(), 1);
4416     }
4417 
4418     // Test where works: All properties specified
4419     {
4420         const json element = R"(
4421             {
4422               "comments": [ "Config file for a FooBar one-chassis system" ],
4423               "rules": [
4424                 {
4425                   "id": "set_voltage_rule",
4426                   "actions": [
4427                     { "pmbus_write_vout_command": { "format": "linear" } }
4428                   ]
4429                 }
4430               ],
4431               "chassis": [
4432                 { "number": 1 },
4433                 { "number": 3 }
4434               ]
4435             }
4436         )"_json;
4437         std::vector<std::unique_ptr<Rule>> rules{};
4438         std::vector<std::unique_ptr<Chassis>> chassis{};
4439         std::tie(rules, chassis) = parseRoot(element);
4440         EXPECT_EQ(rules.size(), 1);
4441         EXPECT_EQ(chassis.size(), 2);
4442     }
4443 
4444     // Test where fails: Element is not an object
4445     try
4446     {
4447         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4448         parseRoot(element);
4449         ADD_FAILURE() << "Should not have reached this line.";
4450     }
4451     catch (const std::invalid_argument& e)
4452     {
4453         EXPECT_STREQ(e.what(), "Element is not an object");
4454     }
4455 
4456     // Test where fails: chassis property not specified
4457     try
4458     {
4459         const json element = R"(
4460             {
4461               "rules": [
4462                 {
4463                   "id": "set_voltage_rule",
4464                   "actions": [
4465                     { "pmbus_write_vout_command": { "format": "linear" } }
4466                   ]
4467                 }
4468               ]
4469             }
4470         )"_json;
4471         parseRoot(element);
4472         ADD_FAILURE() << "Should not have reached this line.";
4473     }
4474     catch (const std::invalid_argument& e)
4475     {
4476         EXPECT_STREQ(e.what(), "Required property missing: chassis");
4477     }
4478 
4479     // Test where fails: Invalid property specified
4480     try
4481     {
4482         const json element = R"(
4483             {
4484               "remarks": [ "Config file for a FooBar one-chassis system" ],
4485               "chassis": [
4486                 { "number": 1 }
4487               ]
4488             }
4489         )"_json;
4490         parseRoot(element);
4491         ADD_FAILURE() << "Should not have reached this line.";
4492     }
4493     catch (const std::invalid_argument& e)
4494     {
4495         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4496     }
4497 }
4498 
4499 TEST(ConfigFileParserTests, ParseRule)
4500 {
4501     // Test where works: comments property specified
4502     {
4503         const json element = R"(
4504             {
4505               "comments": [ "Set voltage rule" ],
4506               "id": "set_voltage_rule",
4507               "actions": [
4508                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4509                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
4510               ]
4511             }
4512         )"_json;
4513         std::unique_ptr<Rule> rule = parseRule(element);
4514         EXPECT_EQ(rule->getID(), "set_voltage_rule");
4515         EXPECT_EQ(rule->getActions().size(), 2);
4516     }
4517 
4518     // Test where works: comments property not specified
4519     {
4520         const json element = R"(
4521             {
4522               "id": "set_voltage_rule",
4523               "actions": [
4524                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4525                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } },
4526                 { "pmbus_write_vout_command": { "volts": 1.05, "format": "linear" } }
4527               ]
4528             }
4529         )"_json;
4530         std::unique_ptr<Rule> rule = parseRule(element);
4531         EXPECT_EQ(rule->getID(), "set_voltage_rule");
4532         EXPECT_EQ(rule->getActions().size(), 3);
4533     }
4534 
4535     // Test where fails: Element is not an object
4536     try
4537     {
4538         const json element = R"( [ "0xFF", "0x01" ] )"_json;
4539         parseRule(element);
4540         ADD_FAILURE() << "Should not have reached this line.";
4541     }
4542     catch (const std::invalid_argument& e)
4543     {
4544         EXPECT_STREQ(e.what(), "Element is not an object");
4545     }
4546 
4547     // Test where fails: id property not specified
4548     try
4549     {
4550         const json element = R"(
4551             {
4552               "actions": [
4553                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4554               ]
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: id");
4563     }
4564 
4565     // Test where fails: id property is invalid
4566     try
4567     {
4568         const json element = R"(
4569             {
4570               "id": "",
4571               "actions": [
4572                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4573               ]
4574             }
4575         )"_json;
4576         parseRule(element);
4577         ADD_FAILURE() << "Should not have reached this line.";
4578     }
4579     catch (const std::invalid_argument& e)
4580     {
4581         EXPECT_STREQ(e.what(), "Element contains an empty string");
4582     }
4583 
4584     // Test where fails: actions property not specified
4585     try
4586     {
4587         const json element = R"(
4588             {
4589               "comments": [ "Set voltage rule" ],
4590               "id": "set_voltage_rule"
4591             }
4592         )"_json;
4593         parseRule(element);
4594         ADD_FAILURE() << "Should not have reached this line.";
4595     }
4596     catch (const std::invalid_argument& e)
4597     {
4598         EXPECT_STREQ(e.what(), "Required property missing: actions");
4599     }
4600 
4601     // Test where fails: actions property is invalid
4602     try
4603     {
4604         const json element = R"(
4605             {
4606               "id": "set_voltage_rule",
4607               "actions": true
4608             }
4609         )"_json;
4610         parseRule(element);
4611         ADD_FAILURE() << "Should not have reached this line.";
4612     }
4613     catch (const std::invalid_argument& e)
4614     {
4615         EXPECT_STREQ(e.what(), "Element is not an array");
4616     }
4617 
4618     // Test where fails: Invalid property specified
4619     try
4620     {
4621         const json element = R"(
4622             {
4623               "remarks": [ "Set voltage rule" ],
4624               "id": "set_voltage_rule",
4625               "actions": [
4626                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4627               ]
4628             }
4629         )"_json;
4630         parseRule(element);
4631         ADD_FAILURE() << "Should not have reached this line.";
4632     }
4633     catch (const std::invalid_argument& e)
4634     {
4635         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4636     }
4637 }
4638 
4639 TEST(ConfigFileParserTests, ParseRuleArray)
4640 {
4641     // Test where works
4642     {
4643         const json element = R"(
4644             [
4645               {
4646                 "id": "set_voltage_rule1",
4647                 "actions": [
4648                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
4649                 ]
4650               },
4651               {
4652                 "id": "set_voltage_rule2",
4653                 "actions": [
4654                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
4655                   { "pmbus_write_vout_command": { "volts": 1.11, "format": "linear" } }
4656                 ]
4657               }
4658             ]
4659         )"_json;
4660         std::vector<std::unique_ptr<Rule>> rules = parseRuleArray(element);
4661         EXPECT_EQ(rules.size(), 2);
4662         EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
4663         EXPECT_EQ(rules[0]->getActions().size(), 1);
4664         EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
4665         EXPECT_EQ(rules[1]->getActions().size(), 2);
4666     }
4667 
4668     // Test where fails: Element is not an array
4669     try
4670     {
4671         const json element = R"( { "id": "set_voltage_rule" } )"_json;
4672         parseRuleArray(element);
4673         ADD_FAILURE() << "Should not have reached this line.";
4674     }
4675     catch (const std::invalid_argument& e)
4676     {
4677         EXPECT_STREQ(e.what(), "Element is not an array");
4678     }
4679 }
4680 
4681 TEST(ConfigFileParserTests, ParseRuleIDOrActionsProperty)
4682 {
4683     // Test where works: actions specified
4684     {
4685         const json element = R"(
4686             {
4687               "actions": [
4688                 { "pmbus_write_vout_command": { "format": "linear" } },
4689                 { "run_rule": "set_voltage_rule" }
4690               ]
4691             }
4692         )"_json;
4693         std::vector<std::unique_ptr<Action>> actions =
4694             parseRuleIDOrActionsProperty(element);
4695         EXPECT_EQ(actions.size(), 2);
4696     }
4697 
4698     // Test where works: rule_id specified
4699     {
4700         const json element = R"(
4701             {
4702               "rule_id": "set_voltage_rule"
4703             }
4704         )"_json;
4705         std::vector<std::unique_ptr<Action>> actions =
4706             parseRuleIDOrActionsProperty(element);
4707         EXPECT_EQ(actions.size(), 1);
4708     }
4709 
4710     // Test where fails: Element is not an object
4711     try
4712     {
4713         const json element = R"( [ "foo", "bar" ] )"_json;
4714         parseRuleIDOrActionsProperty(element);
4715         ADD_FAILURE() << "Should not have reached this line.";
4716     }
4717     catch (const std::invalid_argument& e)
4718     {
4719         EXPECT_STREQ(e.what(), "Element is not an object");
4720     }
4721 
4722     // Test where fails: rule_id is invalid
4723     try
4724     {
4725         const json element = R"(
4726             { "rule_id": 1 }
4727         )"_json;
4728         parseRuleIDOrActionsProperty(element);
4729         ADD_FAILURE() << "Should not have reached this line.";
4730     }
4731     catch (const std::invalid_argument& e)
4732     {
4733         EXPECT_STREQ(e.what(), "Element is not a string");
4734     }
4735 
4736     // Test where fails: actions is invalid
4737     try
4738     {
4739         const json element = R"(
4740             { "actions": 1 }
4741         )"_json;
4742         parseRuleIDOrActionsProperty(element);
4743         ADD_FAILURE() << "Should not have reached this line.";
4744     }
4745     catch (const std::invalid_argument& e)
4746     {
4747         EXPECT_STREQ(e.what(), "Element is not an array");
4748     }
4749 
4750     // Test where fails: Neither rule_id nor actions specified
4751     try
4752     {
4753         const json element = R"(
4754             {
4755               "volts": 1.03
4756             }
4757         )"_json;
4758         parseRuleIDOrActionsProperty(element);
4759         ADD_FAILURE() << "Should not have reached this line.";
4760     }
4761     catch (const std::invalid_argument& e)
4762     {
4763         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4764                                "either rule_id or actions");
4765     }
4766 
4767     // Test where fails: Both rule_id and actions specified
4768     try
4769     {
4770         const json element = R"(
4771             {
4772               "volts": 1.03,
4773               "rule_id": "set_voltage_rule",
4774               "actions": [
4775                 {
4776                   "pmbus_write_vout_command": {
4777                     "format": "linear"
4778                   }
4779                 }
4780               ]
4781             }
4782         )"_json;
4783         parseRuleIDOrActionsProperty(element);
4784         ADD_FAILURE() << "Should not have reached this line.";
4785     }
4786     catch (const std::invalid_argument& e)
4787     {
4788         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4789                                "either rule_id or actions");
4790     }
4791 }
4792 
4793 TEST(ConfigFileParserTests, ParseRunRule)
4794 {
4795     // Test where works
4796     {
4797         const json element = "vdd_regulator";
4798         std::unique_ptr<RunRuleAction> action = parseRunRule(element);
4799         EXPECT_EQ(action->getRuleID(), "vdd_regulator");
4800     }
4801 
4802     // Test where fails: Element is not a string
4803     try
4804     {
4805         const json element = 1;
4806         parseRunRule(element);
4807         ADD_FAILURE() << "Should not have reached this line.";
4808     }
4809     catch (const std::invalid_argument& e)
4810     {
4811         EXPECT_STREQ(e.what(), "Element is not a string");
4812     }
4813 
4814     // Test where fails: Empty string
4815     try
4816     {
4817         const json element = "";
4818         parseRunRule(element);
4819         ADD_FAILURE() << "Should not have reached this line.";
4820     }
4821     catch (const std::invalid_argument& e)
4822     {
4823         EXPECT_STREQ(e.what(), "Element contains an empty string");
4824     }
4825 }
4826 
4827 TEST(ConfigFileParserTests, ParseSensorDataFormat)
4828 {
4829     // Test where works: linear_11
4830     {
4831         const json element = "linear_11";
4832         pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element);
4833         pmbus_utils::SensorDataFormat format =
4834             pmbus_utils::SensorDataFormat::linear_11;
4835         EXPECT_EQ(value, format);
4836     }
4837 
4838     // Test where works: linear_16
4839     {
4840         const json element = "linear_16";
4841         pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element);
4842         pmbus_utils::SensorDataFormat format =
4843             pmbus_utils::SensorDataFormat::linear_16;
4844         EXPECT_EQ(value, format);
4845     }
4846 
4847     // Test where fails: Element is not a sensor data format
4848     try
4849     {
4850         const json element = "foo";
4851         parseSensorDataFormat(element);
4852         ADD_FAILURE() << "Should not have reached this line.";
4853     }
4854     catch (const std::invalid_argument& e)
4855     {
4856         EXPECT_STREQ(e.what(), "Element is not a sensor data format");
4857     }
4858 
4859     // Test where fails: Element is not a string
4860     try
4861     {
4862         const json element = R"( { "foo": "bar" } )"_json;
4863         parseSensorDataFormat(element);
4864         ADD_FAILURE() << "Should not have reached this line.";
4865     }
4866     catch (const std::invalid_argument& e)
4867     {
4868         EXPECT_STREQ(e.what(), "Element is not a string");
4869     }
4870 }
4871 
4872 TEST(ConfigFileParserTests, ParseSensorMonitoring)
4873 {
4874     // Test where works: actions property specified
4875     {
4876         const json element = R"(
4877             {
4878               "actions": [
4879                 { "run_rule": "read_sensors_rule" }
4880               ]
4881             }
4882         )"_json;
4883         std::unique_ptr<SensorMonitoring> sensorMonitoring =
4884             parseSensorMonitoring(element);
4885         EXPECT_EQ(sensorMonitoring->getActions().size(), 1);
4886     }
4887 
4888     // Test where works: rule_id property specified
4889     {
4890         const json element = R"(
4891             {
4892               "comments": [ "comments property" ],
4893               "rule_id": "set_voltage_rule"
4894             }
4895         )"_json;
4896         std::unique_ptr<SensorMonitoring> sensorMonitoring =
4897             parseSensorMonitoring(element);
4898         EXPECT_EQ(sensorMonitoring->getActions().size(), 1);
4899     }
4900 
4901     // Test where fails: actions object is invalid
4902     try
4903     {
4904         const json element = R"(
4905             {
4906               "actions": 1
4907             }
4908         )"_json;
4909         parseSensorMonitoring(element);
4910         ADD_FAILURE() << "Should not have reached this line.";
4911     }
4912     catch (const std::invalid_argument& e)
4913     {
4914         EXPECT_STREQ(e.what(), "Element is not an array");
4915     }
4916 
4917     // Test where fails: rule_id value is invalid
4918     try
4919     {
4920         const json element = R"(
4921             {
4922               "rule_id": 1
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(), "Element is not a string");
4931     }
4932 
4933     // Test where fails: Required actions or rule_id property not specified
4934     try
4935     {
4936         const json element = R"(
4937             {
4938               "comments": [ "comments property" ]
4939             }
4940         )"_json;
4941         parseSensorMonitoring(element);
4942         ADD_FAILURE() << "Should not have reached this line.";
4943     }
4944     catch (const std::invalid_argument& e)
4945     {
4946         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4947                                "either rule_id or actions");
4948     }
4949 
4950     // Test where fails: Required actions or rule_id property both specified
4951     try
4952     {
4953         const json element = R"(
4954             {
4955               "rule_id": "set_voltage_rule",
4956               "actions": [
4957                 { "run_rule": "read_sensors_rule" }
4958               ]
4959             }
4960         )"_json;
4961         parseSensorMonitoring(element);
4962         ADD_FAILURE() << "Should not have reached this line.";
4963     }
4964     catch (const std::invalid_argument& e)
4965     {
4966         EXPECT_STREQ(e.what(), "Invalid property combination: Must contain "
4967                                "either rule_id or actions");
4968     }
4969 
4970     // Test where fails: Element is not an object
4971     try
4972     {
4973         const json element = R"( [ "foo", "bar" ] )"_json;
4974         parseSensorMonitoring(element);
4975         ADD_FAILURE() << "Should not have reached this line.";
4976     }
4977     catch (const std::invalid_argument& e)
4978     {
4979         EXPECT_STREQ(e.what(), "Element is not an object");
4980     }
4981 
4982     // Test where fails: Invalid property specified
4983     try
4984     {
4985         const json element = R"(
4986             {
4987               "foo": "bar",
4988               "actions": [
4989                 { "run_rule": "read_sensors_rule" }
4990               ]
4991             }
4992         )"_json;
4993         parseSensorMonitoring(element);
4994         ADD_FAILURE() << "Should not have reached this line.";
4995     }
4996     catch (const std::invalid_argument& e)
4997     {
4998         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4999     }
5000 }
5001 
5002 TEST(ConfigFileParserTests, ParseSensorType)
5003 {
5004     // Test where works: iout
5005     {
5006         const json element = "iout";
5007         SensorType type = parseSensorType(element);
5008         EXPECT_EQ(type, SensorType::iout);
5009     }
5010 
5011     // Test where works: iout_peak
5012     {
5013         const json element = "iout_peak";
5014         SensorType type = parseSensorType(element);
5015         EXPECT_EQ(type, SensorType::iout_peak);
5016     }
5017 
5018     // Test where works: iout_valley
5019     {
5020         const json element = "iout_valley";
5021         SensorType type = parseSensorType(element);
5022         EXPECT_EQ(type, SensorType::iout_valley);
5023     }
5024 
5025     // Test where works: pout
5026     {
5027         const json element = "pout";
5028         SensorType type = parseSensorType(element);
5029         EXPECT_EQ(type, SensorType::pout);
5030     }
5031 
5032     // Test where works: temperature
5033     {
5034         const json element = "temperature";
5035         SensorType type = parseSensorType(element);
5036         EXPECT_EQ(type, SensorType::temperature);
5037     }
5038 
5039     // Test where works: temperature_peak
5040     {
5041         const json element = "temperature_peak";
5042         SensorType type = parseSensorType(element);
5043         EXPECT_EQ(type, SensorType::temperature_peak);
5044     }
5045 
5046     // Test where works: vout
5047     {
5048         const json element = "vout";
5049         SensorType type = parseSensorType(element);
5050         EXPECT_EQ(type, SensorType::vout);
5051     }
5052 
5053     // Test where works: vout_peak
5054     {
5055         const json element = "vout_peak";
5056         SensorType type = parseSensorType(element);
5057         EXPECT_EQ(type, SensorType::vout_peak);
5058     }
5059 
5060     // Test where works: vout_valley
5061     {
5062         const json element = "vout_valley";
5063         SensorType type = parseSensorType(element);
5064         EXPECT_EQ(type, SensorType::vout_valley);
5065     }
5066 
5067     // Test where fails: Element is not a sensor type
5068     try
5069     {
5070         const json element = "foo";
5071         parseSensorType(element);
5072         ADD_FAILURE() << "Should not have reached this line.";
5073     }
5074     catch (const std::invalid_argument& e)
5075     {
5076         EXPECT_STREQ(e.what(), "Element is not a sensor type");
5077     }
5078 
5079     // Test where fails: Element is not a string
5080     try
5081     {
5082         const json element = R"( { "foo": "bar" } )"_json;
5083         parseSensorType(element);
5084         ADD_FAILURE() << "Should not have reached this line.";
5085     }
5086     catch (const std::invalid_argument& e)
5087     {
5088         EXPECT_STREQ(e.what(), "Element is not a string");
5089     }
5090 }
5091 
5092 TEST(ConfigFileParserTests, ParseSetDevice)
5093 {
5094     // Test where works
5095     {
5096         const json element = "regulator1";
5097         std::unique_ptr<SetDeviceAction> action = parseSetDevice(element);
5098         EXPECT_EQ(action->getDeviceID(), "regulator1");
5099     }
5100 
5101     // Test where fails: Element is not a string
5102     try
5103     {
5104         const json element = 1;
5105         parseSetDevice(element);
5106         ADD_FAILURE() << "Should not have reached this line.";
5107     }
5108     catch (const std::invalid_argument& e)
5109     {
5110         EXPECT_STREQ(e.what(), "Element is not a string");
5111     }
5112 
5113     // Test where fails: Empty string
5114     try
5115     {
5116         const json element = "";
5117         parseSetDevice(element);
5118         ADD_FAILURE() << "Should not have reached this line.";
5119     }
5120     catch (const std::invalid_argument& e)
5121     {
5122         EXPECT_STREQ(e.what(), "Element contains an empty string");
5123     }
5124 }
5125 
5126 TEST(ConfigFileParserTests, ParseString)
5127 {
5128     // Test where works: Empty string
5129     {
5130         const json element = "";
5131         std::string value = parseString(element, true);
5132         EXPECT_EQ(value, "");
5133     }
5134 
5135     // Test where works: Non-empty string
5136     {
5137         const json element = "vdd_regulator";
5138         std::string value = parseString(element, false);
5139         EXPECT_EQ(value, "vdd_regulator");
5140     }
5141 
5142     // Test where fails: Element is not a string
5143     try
5144     {
5145         const json element = R"( { "foo": "bar" } )"_json;
5146         parseString(element);
5147         ADD_FAILURE() << "Should not have reached this line.";
5148     }
5149     catch (const std::invalid_argument& e)
5150     {
5151         EXPECT_STREQ(e.what(), "Element is not a string");
5152     }
5153 
5154     // Test where fails: Empty string
5155     try
5156     {
5157         const json element = "";
5158         parseString(element);
5159         ADD_FAILURE() << "Should not have reached this line.";
5160     }
5161     catch (const std::invalid_argument& e)
5162     {
5163         EXPECT_STREQ(e.what(), "Element contains an empty string");
5164     }
5165 }
5166 
5167 TEST(ConfigFileParserTests, ParseUint8)
5168 {
5169     // Test where works: 0
5170     {
5171         const json element = R"( 0 )"_json;
5172         uint8_t value = parseUint8(element);
5173         EXPECT_EQ(value, 0);
5174     }
5175 
5176     // Test where works: UINT8_MAX
5177     {
5178         const json element = R"( 255 )"_json;
5179         uint8_t value = parseUint8(element);
5180         EXPECT_EQ(value, 255);
5181     }
5182 
5183     // Test where fails: Element is not an integer
5184     try
5185     {
5186         const json element = R"( 1.03 )"_json;
5187         parseUint8(element);
5188         ADD_FAILURE() << "Should not have reached this line.";
5189     }
5190     catch (const std::invalid_argument& e)
5191     {
5192         EXPECT_STREQ(e.what(), "Element is not an integer");
5193     }
5194 
5195     // Test where fails: Value < 0
5196     try
5197     {
5198         const json element = R"( -1 )"_json;
5199         parseUint8(element);
5200         ADD_FAILURE() << "Should not have reached this line.";
5201     }
5202     catch (const std::invalid_argument& e)
5203     {
5204         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
5205     }
5206 
5207     // Test where fails: Value > UINT8_MAX
5208     try
5209     {
5210         const json element = R"( 256 )"_json;
5211         parseUint8(element);
5212         ADD_FAILURE() << "Should not have reached this line.";
5213     }
5214     catch (const std::invalid_argument& e)
5215     {
5216         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
5217     }
5218 }
5219 
5220 TEST(ConfigFileParserTests, ParseUnsignedInteger)
5221 {
5222     // Test where works: 1
5223     {
5224         const json element = R"( 1 )"_json;
5225         unsigned int value = parseUnsignedInteger(element);
5226         EXPECT_EQ(value, 1);
5227     }
5228 
5229     // Test where fails: Element is not an integer
5230     try
5231     {
5232         const json element = R"( 1.5 )"_json;
5233         parseUnsignedInteger(element);
5234         ADD_FAILURE() << "Should not have reached this line.";
5235     }
5236     catch (const std::invalid_argument& e)
5237     {
5238         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
5239     }
5240 
5241     // Test where fails: Value < 0
5242     try
5243     {
5244         const json element = R"( -1 )"_json;
5245         parseUnsignedInteger(element);
5246         ADD_FAILURE() << "Should not have reached this line.";
5247     }
5248     catch (const std::invalid_argument& e)
5249     {
5250         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
5251     }
5252 }
5253 
5254 TEST(ConfigFileParserTests, ParseVoutDataFormat)
5255 {
5256     // Test where works: linear
5257     {
5258         const json element = "linear";
5259         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5260         pmbus_utils::VoutDataFormat format =
5261             pmbus_utils::VoutDataFormat::linear;
5262         EXPECT_EQ(value, format);
5263     }
5264 
5265     // Test where works: vid
5266     {
5267         const json element = "vid";
5268         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5269         pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::vid;
5270         EXPECT_EQ(value, format);
5271     }
5272 
5273     // Test where works: direct
5274     {
5275         const json element = "direct";
5276         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5277         pmbus_utils::VoutDataFormat format =
5278             pmbus_utils::VoutDataFormat::direct;
5279         EXPECT_EQ(value, format);
5280     }
5281 
5282     // Test where works: ieee
5283     {
5284         const json element = "ieee";
5285         pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element);
5286         pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::ieee;
5287         EXPECT_EQ(value, format);
5288     }
5289 
5290     // Test where fails: Element is not a vout data format
5291     try
5292     {
5293         const json element = "foo";
5294         parseVoutDataFormat(element);
5295         ADD_FAILURE() << "Should not have reached this line.";
5296     }
5297     catch (const std::invalid_argument& e)
5298     {
5299         EXPECT_STREQ(e.what(), "Element is not a vout data format");
5300     }
5301 
5302     // Test where fails: Element is not a string
5303     try
5304     {
5305         const json element = R"( { "foo": "bar" } )"_json;
5306         parseVoutDataFormat(element);
5307         ADD_FAILURE() << "Should not have reached this line.";
5308     }
5309     catch (const std::invalid_argument& e)
5310     {
5311         EXPECT_STREQ(e.what(), "Element is not a string");
5312     }
5313 }
5314 
5315 TEST(ConfigFileParserTests, VerifyIsArray)
5316 {
5317     // Test where element is an array
5318     try
5319     {
5320         const json element = R"( [ "foo", "bar" ] )"_json;
5321         verifyIsArray(element);
5322     }
5323     catch (const std::exception& e)
5324     {
5325         ADD_FAILURE() << "Should not have caught exception.";
5326     }
5327 
5328     // Test where element is not an array
5329     try
5330     {
5331         const json element = R"( { "foo": "bar" } )"_json;
5332         verifyIsArray(element);
5333         ADD_FAILURE() << "Should not have reached this line.";
5334     }
5335     catch (const std::invalid_argument& e)
5336     {
5337         EXPECT_STREQ(e.what(), "Element is not an array");
5338     }
5339 }
5340 
5341 TEST(ConfigFileParserTests, VerifyIsObject)
5342 {
5343     // Test where element is an object
5344     try
5345     {
5346         const json element = R"( { "foo": "bar" } )"_json;
5347         verifyIsObject(element);
5348     }
5349     catch (const std::exception& e)
5350     {
5351         ADD_FAILURE() << "Should not have caught exception.";
5352     }
5353 
5354     // Test where element is not an object
5355     try
5356     {
5357         const json element = R"( [ "foo", "bar" ] )"_json;
5358         verifyIsObject(element);
5359         ADD_FAILURE() << "Should not have reached this line.";
5360     }
5361     catch (const std::invalid_argument& e)
5362     {
5363         EXPECT_STREQ(e.what(), "Element is not an object");
5364     }
5365 }
5366 
5367 TEST(ConfigFileParserTests, VerifyPropertyCount)
5368 {
5369     // Test where element has expected number of properties
5370     try
5371     {
5372         const json element = R"(
5373             {
5374               "comments": [ "Set voltage rule" ],
5375               "id": "set_voltage_rule"
5376             }
5377         )"_json;
5378         verifyPropertyCount(element, 2);
5379     }
5380     catch (const std::exception& e)
5381     {
5382         ADD_FAILURE() << "Should not have caught exception.";
5383     }
5384 
5385     // Test where element has unexpected number of properties
5386     try
5387     {
5388         const json element = R"(
5389             {
5390               "comments": [ "Set voltage rule" ],
5391               "id": "set_voltage_rule",
5392               "foo": 1.3
5393             }
5394         )"_json;
5395         verifyPropertyCount(element, 2);
5396         ADD_FAILURE() << "Should not have reached this line.";
5397     }
5398     catch (const std::invalid_argument& e)
5399     {
5400         EXPECT_STREQ(e.what(), "Element contains an invalid property");
5401     }
5402 }
5403