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