1 /**
2  * Copyright c 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 "temporary_file.hpp"
17 
18 #include <stdio.h>    // for popen(), pclose(), fgets()
19 #include <sys/stat.h> // for chmod()
20 #include <sys/wait.h> // for WEXITSTATUS
21 
22 #include <nlohmann/json.hpp>
23 
24 #include <cstdio>
25 #include <fstream>
26 
27 #include <gtest/gtest.h>
28 
29 #define EXPECT_FILE_VALID(configFile) expectFileValid(configFile)
30 #define EXPECT_FILE_INVALID(configFile, expectedErrorMessage,                  \
31                             expectedOutputMessage)                             \
32     expectFileInvalid(configFile, expectedErrorMessage, expectedOutputMessage)
33 #define EXPECT_JSON_VALID(configFileJson) expectJsonValid(configFileJson)
34 #define EXPECT_JSON_INVALID(configFileJson, expectedErrorMessage,              \
35                             expectedOutputMessage)                             \
36     expectJsonInvalid(configFileJson, expectedErrorMessage,                    \
37                       expectedOutputMessage)
38 
39 using namespace phosphor::power::regulators;
40 using json = nlohmann::json;
41 
42 const json validConfigFile = R"(
43     {
44       "comments": [ "Config file for a FooBar one-chassis system" ],
45 
46       "rules": [
47         {
48           "comments": [ "Sets output voltage for a PMBus regulator rail" ],
49           "id": "set_voltage_rule",
50           "actions": [
51             {
52               "pmbus_write_vout_command": {
53                 "format": "linear"
54               }
55             }
56           ]
57         },
58         {
59           "comments": [ "Reads sensors from a PMBus regulator rail" ],
60           "id": "read_sensors_rule",
61           "actions": [
62             {
63               "comments": [ "Read output voltage from READ_VOUT." ],
64               "pmbus_read_sensor": {
65                 "type": "vout",
66                 "command": "0x8B",
67                 "format": "linear_16"
68               }
69             }
70           ]
71         }
72       ],
73 
74       "chassis": [
75         {
76           "comments": [ "Chassis number 1 containing CPUs and memory" ],
77           "number": 1,
78           "inventory_path": "system/chassis",
79           "devices": [
80             {
81               "comments": [ "IR35221 regulator producing the Vdd rail" ],
82               "id": "vdd_regulator",
83               "is_regulator": true,
84               "fru": "system/chassis/motherboard/regulator1",
85               "i2c_interface": {
86                 "bus": 1,
87                 "address": "0x70"
88               },
89               "rails": [
90                 {
91                   "comments": [ "Vdd rail" ],
92                   "id": "vdd",
93                   "configuration": {
94                     "volts": 1.03,
95                     "rule_id": "set_voltage_rule"
96                   },
97                   "sensor_monitoring": {
98                     "rule_id": "read_sensors_rule"
99                   }
100                 }
101               ]
102             }
103           ]
104         }
105       ]
106     }
107 )"_json;
108 
109 std::string getValidationToolCommand(const std::string& configFileName)
110 {
111     std::string command =
112         "../phosphor-regulators/tools/validate-regulators-config.py -s \
113          ../phosphor-regulators/schema/config_schema.json -c ";
114     command += configFileName;
115     return command;
116 }
117 
118 int runToolForOutputWithCommand(std::string command,
119                                 std::string& standardOutput,
120                                 std::string& standardError)
121 {
122     // run the validation tool with the temporary file and return the output
123     // of the validation tool.
124     TemporaryFile tmpFile;
125     command += " 2> " + tmpFile.getPath().string();
126     // get the jsonschema print from validation tool.
127     char buffer[256];
128     std::string result;
129     // to get the stdout from the validation tool.
130     FILE* pipe = popen(command.c_str(), "r");
131     if (!pipe)
132     {
133         throw std::runtime_error("popen() failed!");
134     }
135     while (!std::feof(pipe))
136     {
137         if (fgets(buffer, sizeof buffer, pipe) != NULL)
138         {
139             result += buffer;
140         }
141     }
142     int returnValue = pclose(pipe);
143     // Check if pclose() failed
144     if (returnValue == -1)
145     {
146         // unable to close pipe.  Print error and exit function.
147         throw std::runtime_error("pclose() failed!");
148     }
149     std::string firstLine = result.substr(0, result.find('\n'));
150     standardOutput = firstLine;
151     // Get command exit status from return value
152     int exitStatus = WEXITSTATUS(returnValue);
153 
154     // Read the standardError from tmpFile.
155     std::ifstream input(tmpFile.getPath());
156     std::string line;
157 
158     if (std::getline(input, line))
159     {
160         standardError = line;
161     }
162 
163     return exitStatus;
164 }
165 
166 int runToolForOutput(const std::string& configFileName, std::string& output,
167                      std::string& error)
168 {
169     std::string command = getValidationToolCommand(configFileName);
170     return runToolForOutputWithCommand(command, output, error);
171 }
172 
173 void expectFileValid(const std::string& configFileName)
174 {
175     std::string errorMessage;
176     std::string outputMessage;
177     EXPECT_EQ(runToolForOutput(configFileName, outputMessage, errorMessage), 0);
178     EXPECT_EQ(errorMessage, "");
179     EXPECT_EQ(outputMessage, "");
180 }
181 
182 void expectFileInvalid(const std::string& configFileName,
183                        const std::string& expectedErrorMessage,
184                        const std::string& expectedOutputMessage)
185 {
186     std::string errorMessage;
187     std::string outputMessage;
188     EXPECT_EQ(runToolForOutput(configFileName, outputMessage, errorMessage), 1);
189     EXPECT_EQ(errorMessage, expectedErrorMessage);
190     EXPECT_EQ(outputMessage, expectedOutputMessage);
191 }
192 
193 void writeDataToFile(const json configFileJson, std::string fileName)
194 {
195     std::string jsonData = configFileJson.dump();
196     std::ofstream out(fileName);
197     out << jsonData;
198     out.close();
199 }
200 
201 void expectJsonValid(const json configFileJson)
202 {
203     TemporaryFile tmpFile;
204     std::string fileName = tmpFile.getPath().string();
205     writeDataToFile(configFileJson, fileName);
206 
207     EXPECT_FILE_VALID(fileName);
208 }
209 
210 void expectJsonInvalid(const json configFileJson,
211                        const std::string& expectedErrorMessage,
212                        const std::string& expectedOutputMessage)
213 {
214     TemporaryFile tmpFile;
215     std::string fileName = tmpFile.getPath().string();
216     writeDataToFile(configFileJson, fileName);
217 
218     EXPECT_FILE_INVALID(fileName, expectedErrorMessage, expectedOutputMessage);
219 }
220 
221 void expectCommandLineSyntax(const std::string& expectedErrorMessage,
222                              const std::string& expectedOutputMessage,
223                              const std::string& command, int status)
224 {
225     std::string errorMessage;
226     std::string outputMessage;
227     EXPECT_EQ(runToolForOutputWithCommand(command, outputMessage, errorMessage),
228               status);
229     EXPECT_EQ(errorMessage, expectedErrorMessage);
230     EXPECT_EQ(outputMessage, expectedOutputMessage);
231 }
232 
233 TEST(ValidateRegulatorsConfigTest, Action)
234 {
235     // Valid: Comments property not specified
236     {
237         json configFile = validConfigFile;
238         EXPECT_JSON_VALID(configFile);
239     }
240     // Valid: Comments property specified
241     {
242         json configFile = validConfigFile;
243         configFile["rules"][0]["actions"][0]["comments"][0] =
244             "Set VOUT_COMMAND";
245         EXPECT_JSON_VALID(configFile);
246     }
247     // Valid: and action type specified
248     {
249         json configFile = validConfigFile;
250         json andAction =
251             R"(
252                 {
253                  "and": [
254                     { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
255                     { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
256                   ]
257                 }
258             )"_json;
259         configFile["rules"][0]["actions"].push_back(andAction);
260         EXPECT_JSON_VALID(configFile);
261     }
262     // Valid: compare_presence action type specified
263     {
264         json configFile = validConfigFile;
265         configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] =
266             "system/chassis/motherboard/regulator2";
267         configFile["rules"][0]["actions"][1]["compare_presence"]["value"] =
268             true;
269         EXPECT_JSON_VALID(configFile);
270     }
271     // Valid: compare_vpd action type specified
272     {
273         json configFile = validConfigFile;
274         configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] =
275             "system/chassis/motherboard/regulator2";
276         configFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "CCIN";
277         configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = "2D35";
278         EXPECT_JSON_VALID(configFile);
279     }
280     // Valid: i2c_compare_bit action type specified
281     {
282         json configFile = validConfigFile;
283         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] =
284             "0xA0";
285         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 3;
286         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 1;
287         EXPECT_JSON_VALID(configFile);
288     }
289     // Valid: i2c_compare_byte action type specified
290     {
291         json configFile = validConfigFile;
292         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
293             "0x82";
294         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
295             "0x40";
296         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] =
297             "0x7F";
298         EXPECT_JSON_VALID(configFile);
299     }
300     // Valid: i2c_compare_bytes action type specified
301     {
302         json configFile = validConfigFile;
303         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
304             "0x82";
305         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = {
306             "0x02", "0x73"};
307         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = {
308             "0x7F", "0x7F"};
309         EXPECT_JSON_VALID(configFile);
310     }
311     // Valid: i2c_write_bit action type specified
312     {
313         json configFile = validConfigFile;
314         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] =
315             "0xA0";
316         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3;
317         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 1;
318         EXPECT_JSON_VALID(configFile);
319     }
320     // Valid: i2c_write_byte action type specified
321     {
322         json configFile = validConfigFile;
323         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
324             "0x82";
325         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] =
326             "0x40";
327         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x7F";
328         EXPECT_JSON_VALID(configFile);
329     }
330     // Valid: i2c_write_bytes action type specified
331     {
332         json configFile = validConfigFile;
333         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
334             "0x82";
335         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = {
336             "0x02", "0x73"};
337         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = {
338             "0x7F", "0x7F"};
339         EXPECT_JSON_VALID(configFile);
340     }
341     // Valid: if action type specified
342     {
343         json configFile = validConfigFile;
344         configFile["rules"][2]["actions"][0]["if"]["condition"]["run_rule"] =
345             "set_voltage_rule";
346         configFile["rules"][2]["actions"][0]["if"]["then"][0]["run_rule"] =
347             "read_sensors_rule";
348         configFile["rules"][2]["actions"][0]["if"]["else"][0]["run_rule"] =
349             "read_sensors_rule";
350         configFile["rules"][2]["id"] = "rule_if";
351         EXPECT_JSON_VALID(configFile);
352     }
353     // Valid: not action type specified
354     {
355         json configFile = validConfigFile;
356         configFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]
357                   ["register"] = "0xA0";
358         configFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]
359                   ["value"] = "0xFF";
360         EXPECT_JSON_VALID(configFile);
361     }
362     // Valid: or action type specified
363     {
364         json configFile = validConfigFile;
365         configFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]
366                   ["register"] = "0xA0";
367         configFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]
368                   ["value"] = "0x00";
369         configFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]
370                   ["register"] = "0xA1";
371         configFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]
372                   ["value"] = "0x00";
373         EXPECT_JSON_VALID(configFile);
374     }
375     // Valid: pmbus_read_sensor and pmbus_write_vout_command action type
376     // specified
377     {
378         EXPECT_JSON_VALID(validConfigFile);
379     }
380     // Valid: run_rule action type specified
381     {
382         json configFile = validConfigFile;
383         configFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule";
384         EXPECT_JSON_VALID(configFile);
385     }
386     // Valid: set_device action type specified
387     {
388         json configFile = validConfigFile;
389         configFile["rules"][0]["actions"][1]["set_device"] = "vdd_regulator";
390         EXPECT_JSON_VALID(configFile);
391     }
392     // Invalid: Wrong data type for comments (should be array of string)
393     {
394         json configFile = validConfigFile;
395         configFile["rules"][0]["actions"][0]["comments"] = true;
396         EXPECT_JSON_INVALID(configFile, "Validation failed.",
397                             "True is not of type 'array'");
398     }
399     // Invalid: Wrong data type for action type (such as "i2c_write_byte": true)
400     {
401         json configFile = validConfigFile;
402         configFile["rules"][0]["actions"][1]["i2c_write_byte"] = true;
403         EXPECT_JSON_INVALID(configFile, "Validation failed.",
404                             "True is not of type 'object'");
405     }
406     // Invalid: Empty comments array
407     {
408         json configFile = validConfigFile;
409         configFile["rules"][0]["actions"][0]["comments"] = json::array();
410         EXPECT_JSON_INVALID(configFile, "Validation failed.",
411                             "[] is too short");
412     }
413     // Invalid: Comments array has wrong element type (should be string)
414     {
415         json configFile = validConfigFile;
416         configFile["rules"][0]["actions"][0]["comments"][0] = true;
417         EXPECT_JSON_INVALID(configFile, "Validation failed.",
418                             "True is not of type 'string'");
419     }
420     // Invalid: No action type specified
421     {
422         json configFile = validConfigFile;
423         configFile["rules"][0]["actions"][1]["comments"][0] =
424             "Check if bit 3 is on";
425         EXPECT_JSON_INVALID(configFile, "Validation failed.",
426                             "'and' is a required property");
427     }
428     // Invalid: Multiple action types specified (such as both 'compare_presence'
429     // and 'pmbus_write_vout_command')
430     {
431         json configFile = validConfigFile;
432         configFile["rules"][0]["actions"][0]["compare_presence"]["value"] =
433             true;
434         EXPECT_JSON_INVALID(
435             configFile, "Validation failed.",
436             "{'compare_presence': {'value': True}, 'pmbus_write_vout_command': "
437             "{'format': 'linear'}} is valid under each of {'required': "
438             "['pmbus_write_vout_command']}, {'required': "
439             "['compare_presence']}");
440     }
441     // Invalid: Unexpected property specified (like 'foo')
442     {
443         json configFile = validConfigFile;
444         configFile["rules"][0]["actions"][1]["foo"] = "foo";
445         EXPECT_JSON_INVALID(
446             configFile, "Validation failed.",
447             "Additional properties are not allowed ('foo' was unexpected)");
448     }
449 }
450 TEST(ValidateRegulatorsConfigTest, And)
451 {
452     // Valid.
453     {
454         json configFile = validConfigFile;
455         json andAction =
456             R"(
457                 {
458                  "and": [
459                     { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } },
460                     { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } }
461                   ]
462                 }
463             )"_json;
464         configFile["rules"][0]["actions"].push_back(andAction);
465         EXPECT_JSON_VALID(configFile);
466     }
467 
468     // Invalid: actions property value is an empty array.
469     {
470         json configFile = validConfigFile;
471         json andAction =
472             R"(
473                 {
474                  "and": []
475                 }
476             )"_json;
477         configFile["rules"][0]["actions"].push_back(andAction);
478         EXPECT_JSON_INVALID(configFile, "Validation failed.",
479                             "[] is too short");
480     }
481 
482     // Invalid: actions property has incorrect value data type.
483     {
484         json configFile = validConfigFile;
485         json andAction =
486             R"(
487                 {
488                  "and": true
489                 }
490             )"_json;
491         configFile["rules"][0]["actions"].push_back(andAction);
492         EXPECT_JSON_INVALID(configFile, "Validation failed.",
493                             "True is not of type 'array'");
494     }
495 
496     // Invalid: actions property value contains wrong element type
497     {
498         json configFile = validConfigFile;
499         json andAction =
500             R"(
501                 {
502                  "and": ["foo"]
503                 }
504             )"_json;
505         configFile["rules"][0]["actions"].push_back(andAction);
506         EXPECT_JSON_INVALID(configFile, "Validation failed.",
507                             "'foo' is not of type 'object'");
508     }
509 }
510 TEST(ValidateRegulatorsConfigTest, Chassis)
511 {
512     // Valid: test chassis.
513     {
514         json configFile = validConfigFile;
515         EXPECT_JSON_VALID(configFile);
516     }
517     // Valid: test chassis with only required properties.
518     {
519         json configFile = validConfigFile;
520         configFile["chassis"][0].erase("comments");
521         configFile["chassis"][0].erase("inventory_path");
522         configFile["chassis"][0].erase("devices");
523         EXPECT_JSON_VALID(configFile);
524     }
525     // Invalid: test chassis with no number.
526     {
527         json configFile = validConfigFile;
528         configFile["chassis"][0].erase("number");
529         EXPECT_JSON_INVALID(configFile, "Validation failed.",
530                             "'number' is a required property");
531     }
532     // Invalid: test chassis with property comments wrong type.
533     {
534         json configFile = validConfigFile;
535         configFile["chassis"][0]["comments"] = true;
536         EXPECT_JSON_INVALID(configFile, "Validation failed.",
537                             "True is not of type 'array'");
538     }
539     // Invalid: test chassis with property number wrong type.
540     {
541         json configFile = validConfigFile;
542         configFile["chassis"][0]["number"] = 1.3;
543         EXPECT_JSON_INVALID(configFile, "Validation failed.",
544                             "1.3 is not of type 'integer'");
545     }
546     // Invalid: test chassis with property inventory_path wrong type.
547     {
548         json configFile = validConfigFile;
549         configFile["chassis"][0]["inventory_path"] = 2;
550         EXPECT_JSON_INVALID(configFile, "Validation failed.",
551                             "2 is not of type 'string'");
552     }
553     // Invalid: test chassis with property devices wrong type.
554     {
555         json configFile = validConfigFile;
556         configFile["chassis"][0]["devices"] = true;
557         EXPECT_JSON_INVALID(configFile, "Validation failed.",
558                             "True is not of type 'array'");
559     }
560     // Invalid: test chassis with property comments empty array.
561     {
562         json configFile = validConfigFile;
563         configFile["chassis"][0]["comments"] = json::array();
564         EXPECT_JSON_INVALID(configFile, "Validation failed.",
565                             "[] is too short");
566     }
567     // Invalid: test chassis with property devices empty array.
568     {
569         json configFile = validConfigFile;
570         configFile["chassis"][0]["devices"] = json::array();
571         EXPECT_JSON_INVALID(configFile, "Validation failed.",
572                             "[] is too short");
573     }
574     // Invalid: test chassis with property number less than 1.
575     {
576         json configFile = validConfigFile;
577         configFile["chassis"][0]["number"] = 0;
578         EXPECT_JSON_INVALID(configFile, "Validation failed.",
579                             "0 is less than the minimum of 1");
580     }
581     // Invalid: test chassis with property inventory_path empty string.
582     {
583         json configFile = validConfigFile;
584         configFile["chassis"][0]["inventory_path"] = "";
585         EXPECT_JSON_INVALID(configFile, "Validation failed.",
586                             "'' is too short");
587     }
588 }
589 TEST(ValidateRegulatorsConfigTest, ComparePresence)
590 {
591     json comparePresenceFile = validConfigFile;
592     comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["fru"] =
593         "system/chassis/motherboard/regulator2";
594     comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["value"] =
595         true;
596     // Valid.
597     {
598         json configFile = comparePresenceFile;
599         EXPECT_JSON_VALID(configFile);
600     }
601 
602     // Invalid: no FRU property.
603     {
604         json configFile = comparePresenceFile;
605         configFile["rules"][0]["actions"][1]["compare_presence"].erase("fru");
606         EXPECT_JSON_INVALID(configFile, "Validation failed.",
607                             "'fru' is a required property");
608     }
609 
610     // Invalid: FRU property length is string less than 1.
611     {
612         json configFile = comparePresenceFile;
613         configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = "";
614         EXPECT_JSON_INVALID(configFile, "Validation failed.",
615                             "'' is too short");
616     }
617 
618     // Invalid: no value property.
619     {
620         json configFile = comparePresenceFile;
621         configFile["rules"][0]["actions"][1]["compare_presence"].erase("value");
622         EXPECT_JSON_INVALID(configFile, "Validation failed.",
623                             "'value' is a required property");
624     }
625 
626     // Invalid: value property type is not boolean.
627     {
628         json configFile = comparePresenceFile;
629         configFile["rules"][0]["actions"][1]["compare_presence"]["value"] = "1";
630         EXPECT_JSON_INVALID(configFile, "Validation failed.",
631                             "'1' is not of type 'boolean'");
632     }
633 
634     // Invalid: FRU property type is not string.
635     {
636         json configFile = comparePresenceFile;
637         configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = 1;
638         EXPECT_JSON_INVALID(configFile, "Validation failed.",
639                             "1 is not of type 'string'");
640     }
641 }
642 TEST(ValidateRegulatorsConfigTest, CompareVpd)
643 {
644     json compareVpdFile = validConfigFile;
645     compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] =
646         "system/chassis/motherboard/regulator2";
647     compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "CCIN";
648     compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = "2D35";
649 
650     // Valid.
651     {
652         json configFile = compareVpdFile;
653         EXPECT_JSON_VALID(configFile);
654     }
655     // Valid, using byte_values.
656     {
657         json configFile = compareVpdFile;
658         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value");
659         configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = {
660             "0x01", "0x02"};
661         EXPECT_JSON_VALID(configFile);
662     }
663 
664     // Invalid: no FRU property.
665     {
666         json configFile = compareVpdFile;
667         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("fru");
668         EXPECT_JSON_INVALID(configFile, "Validation failed.",
669                             "'fru' is a required property");
670     }
671 
672     // Invalid: no keyword property.
673     {
674         json configFile = compareVpdFile;
675         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("keyword");
676         EXPECT_JSON_INVALID(configFile, "Validation failed.",
677                             "'keyword' is a required property");
678     }
679 
680     // Invalid: no value property.
681     {
682         json configFile = compareVpdFile;
683         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value");
684         EXPECT_JSON_INVALID(configFile, "Validation failed.",
685                             "'value' is a required property");
686     }
687 
688     // Invalid: property FRU wrong type.
689     {
690         json configFile = compareVpdFile;
691         configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = 1;
692         EXPECT_JSON_INVALID(configFile, "Validation failed.",
693                             "1 is not of type 'string'");
694     }
695 
696     // Invalid: property FRU is string less than 1.
697     {
698         json configFile = compareVpdFile;
699         configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = "";
700         EXPECT_JSON_INVALID(configFile, "Validation failed.",
701                             "'' is too short");
702     }
703 
704     // Invalid: property keyword is not "CCIN", "Manufacturer", "Model",
705     // "PartNumber", "HW"
706     {
707         json configFile = compareVpdFile;
708         configFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] =
709             "Number";
710         EXPECT_JSON_INVALID(configFile, "Validation failed.",
711                             "'Number' is not one of ['CCIN', "
712                             "'Manufacturer', 'Model', 'PartNumber', 'HW']");
713     }
714 
715     // Invalid: property value wrong type.
716     {
717         json configFile = compareVpdFile;
718         configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = 1;
719         EXPECT_JSON_INVALID(configFile, "Validation failed.",
720                             "1 is not of type 'string'");
721     }
722 
723     // Invalid: property byte_values has wrong type
724     {
725         json configFile = compareVpdFile;
726         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value");
727         configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] =
728             "0x50";
729         EXPECT_JSON_INVALID(configFile, "Validation failed.",
730                             "'0x50' is not of type 'array'");
731     }
732 
733     // Invalid: property byte_values is empty
734     {
735         json configFile = compareVpdFile;
736         configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value");
737         configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] =
738             json::array();
739         EXPECT_JSON_INVALID(configFile, "Validation failed.",
740                             "[] is too short");
741     }
742 
743     // Invalid: properties byte_values and value both exist
744     {
745         json configFile = compareVpdFile;
746         configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = {
747             "0x01", "0x02"};
748         EXPECT_JSON_INVALID(
749             configFile, "Validation failed.",
750             "{'byte_values': ['0x01', '0x02'], 'fru': "
751             "'system/chassis/motherboard/regulator2', 'keyword': 'CCIN', "
752             "'value': '2D35'} is valid under each of {'required': "
753             "['byte_values']}, {'required': ['value']}");
754     }
755 }
756 TEST(ValidateRegulatorsConfigTest, ConfigFile)
757 {
758     // Valid: Only required properties specified
759     {
760         json configFile;
761         configFile["chassis"][0]["number"] = 1;
762         EXPECT_JSON_VALID(configFile);
763     }
764     // Valid: All properties specified
765     {
766         json configFile = validConfigFile;
767         EXPECT_JSON_VALID(configFile);
768     }
769     // Invalid: Required chassis property not specified
770     {
771         json configFile = validConfigFile;
772         configFile.erase("chassis");
773         EXPECT_JSON_INVALID(configFile, "Validation failed.",
774                             "'chassis' is a required property");
775     }
776     // Invalid: Wrong data type for comments
777     {
778         json configFile = validConfigFile;
779         configFile["comments"] = true;
780         EXPECT_JSON_INVALID(configFile, "Validation failed.",
781                             "True is not of type 'array'");
782     }
783     // Invalid: Wrong data type for rules
784     {
785         json configFile = validConfigFile;
786         configFile["rules"] = true;
787         EXPECT_JSON_INVALID(configFile, "Validation failed.",
788                             "True is not of type 'array'");
789     }
790     // Invalid: Wrong data type for chassis
791     {
792         json configFile = validConfigFile;
793         configFile["chassis"] = true;
794         EXPECT_JSON_INVALID(configFile, "Validation failed.",
795                             "True is not of type 'array'");
796     }
797     // Invalid: Empty comments array;
798     {
799         json configFile = validConfigFile;
800         configFile["comments"] = json::array();
801         EXPECT_JSON_INVALID(configFile, "Validation failed.",
802                             "[] is too short");
803     }
804     // Invalid: Empty rules array
805     {
806         json configFile = validConfigFile;
807         configFile["rules"] = json::array();
808         EXPECT_JSON_INVALID(configFile, "Validation failed.",
809                             "[] is too short");
810     }
811     // Invalid: Empty chassis array
812     {
813         json configFile = validConfigFile;
814         configFile["chassis"] = json::array();
815         EXPECT_JSON_INVALID(configFile, "Validation failed.",
816                             "[] is too short");
817     }
818     // Invalid: Comments array has wrong element type (should be string)
819     {
820         json configFile = validConfigFile;
821         configFile["comments"][0] = true;
822         EXPECT_JSON_INVALID(configFile, "Validation failed.",
823                             "True is not of type 'string'");
824     }
825     // Invalid: Rules array has wrong element type (should be rule)
826     {
827         json configFile = validConfigFile;
828         configFile["rules"][0] = true;
829         EXPECT_JSON_INVALID(configFile, "Validation failed.",
830                             "True is not of type 'object'");
831     }
832     // Invalid: Chassis array has wrong element type (should be chassis)
833     {
834         json configFile = validConfigFile;
835         configFile["chassis"][0] = true;
836         EXPECT_JSON_INVALID(configFile, "Validation failed.",
837                             "True is not of type 'object'");
838     }
839     // Invalid: Unexpected property specified
840     {
841         json configFile = validConfigFile;
842         configFile["foo"] = json::array();
843         EXPECT_JSON_INVALID(
844             configFile, "Validation failed.",
845             "Additional properties are not allowed ('foo' was unexpected)");
846     }
847 }
848 TEST(ValidateRegulatorsConfigTest, Configuration)
849 {
850     json configurationFile = validConfigFile;
851     configurationFile["chassis"][0]["devices"][0]["configuration"]["comments"]
852                      [0] = "Set rail to 1.25V using standard rule";
853     configurationFile["chassis"][0]["devices"][0]["configuration"]["volts"] =
854         1.25;
855     configurationFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] =
856         "set_voltage_rule";
857     // Valid: test configuration with property rule_id and with no actions.
858     {
859         json configFile = configurationFile;
860         configFile["chassis"][0]["devices"][0]["configuration"]["comments"][1] =
861             "test multiple array elements in comments.";
862         EXPECT_JSON_VALID(configFile);
863     }
864     // Valid: test configuration with property actions and with no rule_id.
865     {
866         json configFile = configurationFile;
867         configFile["chassis"][0]["devices"][0]["configuration"].erase(
868             "rule_id");
869         configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0]
870                   ["compare_presence"]["fru"] =
871                       "system/chassis/motherboard/cpu3";
872         configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0]
873                   ["compare_presence"]["value"] = true;
874         EXPECT_JSON_VALID(configFile);
875     }
876     // Valid: comments not specified (optional property).
877     {
878         json configFile = configurationFile;
879         configFile["chassis"][0]["devices"][0]["configuration"].erase(
880             "comments");
881         EXPECT_JSON_VALID(configFile);
882     }
883     // Valid: volts not specified (optional property).
884     {
885         json configFile = configurationFile;
886         configFile["chassis"][0]["devices"][0]["configuration"].erase("volts");
887         EXPECT_JSON_VALID(configFile);
888     }
889     // Valid: configuration is property of a rail (vs. a device).
890     {
891         json configFile = validConfigFile;
892         configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"]
893                   ["comments"][0] = "Set rail to 1.25V using standard rule";
894         configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"]
895                   ["volts"] = 1.25;
896         configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"]
897                   ["rule_id"] = "set_voltage_rule";
898         EXPECT_JSON_VALID(configFile);
899     }
900     // Invalid: comments property has wrong data type (not an array).
901     {
902         json configFile = configurationFile;
903         configFile["chassis"][0]["devices"][0]["configuration"]["comments"] = 1;
904         EXPECT_JSON_INVALID(configFile, "Validation failed.",
905                             "1 is not of type 'array'");
906     }
907     // Invalid: test configuration with both actions and rule_id properties.
908     {
909         json configFile = configurationFile;
910         configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0]
911                   ["compare_presence"]["fru"] =
912                       "system/chassis/motherboard/cpu3";
913         configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0]
914                   ["compare_presence"]["value"] = true;
915         EXPECT_JSON_INVALID(
916             configFile, "Validation failed.",
917             "{'actions': [{'compare_presence': {'fru': "
918             "'system/chassis/motherboard/cpu3', 'value': True}}], 'comments': "
919             "['Set rail to 1.25V using standard rule'], 'rule_id': "
920             "'set_voltage_rule', 'volts': 1.25} is valid under each of "
921             "{'required': ['actions']}, {'required': ['rule_id']}");
922     }
923     // Invalid: test configuration with no rule_id and actions.
924     {
925         json configFile = configurationFile;
926         configFile["chassis"][0]["devices"][0]["configuration"].erase(
927             "rule_id");
928         EXPECT_JSON_INVALID(configFile, "Validation failed.",
929                             "'rule_id' is a required property");
930     }
931     // Invalid: test configuration with property volts wrong type.
932     {
933         json configFile = configurationFile;
934         configFile["chassis"][0]["devices"][0]["configuration"]["volts"] = true;
935         EXPECT_JSON_INVALID(configFile, "Validation failed.",
936                             "True is not of type 'number'");
937     }
938     // Invalid: test configuration with property rule_id wrong type.
939     {
940         json configFile = configurationFile;
941         configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] =
942             true;
943         EXPECT_JSON_INVALID(configFile, "Validation failed.",
944                             "True is not of type 'string'");
945     }
946     // Invalid: test configuration with property actions wrong type.
947     {
948         json configFile = configurationFile;
949         configFile["chassis"][0]["devices"][0]["configuration"].erase(
950             "rule_id");
951         configFile["chassis"][0]["devices"][0]["configuration"]["actions"] =
952             true;
953         EXPECT_JSON_INVALID(configFile, "Validation failed.",
954                             "True is not of type 'array'");
955     }
956     // Invalid: test configuration with property comments empty array.
957     {
958         json configFile = configurationFile;
959         configFile["chassis"][0]["devices"][0]["configuration"]["comments"] =
960             json::array();
961         EXPECT_JSON_INVALID(configFile, "Validation failed.",
962                             "[] is too short");
963     }
964     // Invalid: test configuration with property rule_id wrong format.
965     {
966         json configFile = configurationFile;
967         configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] =
968             "id!";
969         EXPECT_JSON_INVALID(configFile, "Validation failed.",
970                             "'id!' does not match '^[A-Za-z0-9_]+$'");
971     }
972     // Invalid: test configuration with property actions empty array.
973     {
974         json configFile = configurationFile;
975         configFile["chassis"][0]["devices"][0]["configuration"].erase(
976             "rule_id");
977         configFile["chassis"][0]["devices"][0]["configuration"]["actions"] =
978             json::array();
979         EXPECT_JSON_INVALID(configFile, "Validation failed.",
980                             "[] is too short");
981     }
982 }
983 TEST(ValidateRegulatorsConfigTest, Device)
984 {
985 
986     // Valid: test devices.
987     {
988         json configFile = validConfigFile;
989         EXPECT_JSON_VALID(configFile);
990     }
991     // Valid: test devices with required properties.
992     {
993         json configFile = validConfigFile;
994         configFile["chassis"][0]["devices"][0].erase("comments");
995         configFile["chassis"][0]["devices"][0].erase("presence_detection");
996         configFile["chassis"][0]["devices"][0].erase("configuration");
997         configFile["chassis"][0]["devices"][0].erase("rails");
998         EXPECT_JSON_VALID(configFile);
999     }
1000     // Invalid: test devices with no id.
1001     {
1002         json configFile = validConfigFile;
1003         configFile["chassis"][0]["devices"][0].erase("id");
1004         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1005                             "'id' is a required property");
1006     }
1007     // Invalid: test devices with no is_regulator.
1008     {
1009         json configFile = validConfigFile;
1010         configFile["chassis"][0]["devices"][0].erase("is_regulator");
1011         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1012                             "'is_regulator' is a required property");
1013     }
1014     // Invalid: test devices with no fru.
1015     {
1016         json configFile = validConfigFile;
1017         configFile["chassis"][0]["devices"][0].erase("fru");
1018         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1019                             "'fru' is a required property");
1020     }
1021     // Invalid: test devices with no i2c_interface.
1022     {
1023         json configFile = validConfigFile;
1024         configFile["chassis"][0]["devices"][0].erase("i2c_interface");
1025         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1026                             "'i2c_interface' is a required property");
1027     }
1028     // Invalid: test devices with property comments wrong type.
1029     {
1030         json configFile = validConfigFile;
1031         configFile["chassis"][0]["devices"][0]["comments"] = true;
1032         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1033                             "True is not of type 'array'");
1034     }
1035     // Invalid: test devices with property id wrong type.
1036     {
1037         json configFile = validConfigFile;
1038         configFile["chassis"][0]["devices"][0]["id"] = true;
1039         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1040                             "True is not of type 'string'");
1041     }
1042     // Invalid: test devices with property is_regulator wrong type.
1043     {
1044         json configFile = validConfigFile;
1045         configFile["chassis"][0]["devices"][0]["is_regulator"] = 1;
1046         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1047                             "1 is not of type 'boolean'");
1048     }
1049     // Invalid: test devices with property fru wrong type.
1050     {
1051         json configFile = validConfigFile;
1052         configFile["chassis"][0]["devices"][0]["fru"] = true;
1053         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1054                             "True is not of type 'string'");
1055     }
1056     // Invalid: test devices with property i2c_interface wrong type.
1057     {
1058         json configFile = validConfigFile;
1059         configFile["chassis"][0]["devices"][0]["i2c_interface"] = true;
1060         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1061                             "True is not of type 'object'");
1062     }
1063     // Invalid: test devices with property presence_detection wrong
1064     // type.
1065     {
1066         json configFile = validConfigFile;
1067         configFile["chassis"][0]["devices"][0]["presence_detection"] = true;
1068         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1069                             "True is not of type 'object'");
1070     }
1071     // Invalid: test devices with property configuration wrong type.
1072     {
1073         json configFile = validConfigFile;
1074         configFile["chassis"][0]["devices"][0]["configuration"] = true;
1075         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1076                             "True is not of type 'object'");
1077     }
1078     // Invalid: test devices with property rails wrong type.
1079     {
1080         json configFile = validConfigFile;
1081         configFile["chassis"][0]["devices"][0]["rails"] = true;
1082         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1083                             "True is not of type 'array'");
1084     }
1085     // Invalid: test devices with property comments empty array.
1086     {
1087         json configFile = validConfigFile;
1088         configFile["chassis"][0]["devices"][0]["comments"] = json::array();
1089         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1090                             "[] is too short");
1091     }
1092     // Invalid: test devices with property fru length less than 1.
1093     {
1094         json configFile = validConfigFile;
1095         configFile["chassis"][0]["devices"][0]["fru"] = "";
1096         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1097                             "'' is too short");
1098     }
1099     // Invalid: test devices with property id wrong format.
1100     {
1101         json configFile = validConfigFile;
1102         configFile["chassis"][0]["devices"][0]["id"] = "id#";
1103         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1104                             "'id#' does not match '^[A-Za-z0-9_]+$'");
1105     }
1106     // Invalid: test devices with property rails empty array.
1107     {
1108         json configFile = validConfigFile;
1109         configFile["chassis"][0]["devices"][0]["rails"] = json::array();
1110         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1111                             "[] is too short");
1112     }
1113 }
1114 TEST(ValidateRegulatorsConfigTest, I2CCompareBit)
1115 {
1116     json i2cCompareBitFile = validConfigFile;
1117     i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] =
1118         "0xA0";
1119     i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] =
1120         3;
1121     i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 1;
1122     // Valid: test rule actions i2c_compare_bit.
1123     {
1124         json configFile = i2cCompareBitFile;
1125         EXPECT_JSON_VALID(configFile);
1126     }
1127     // Invalid: test i2c_compare_bit with no register.
1128     {
1129         json configFile = i2cCompareBitFile;
1130         configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase(
1131             "register");
1132         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1133                             "'register' is a required property");
1134     }
1135     // Invalid: test i2c_compare_bit with no position.
1136     {
1137         json configFile = i2cCompareBitFile;
1138         configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase(
1139             "position");
1140         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1141                             "'position' is a required property");
1142     }
1143     // Invalid: test i2c_compare_bit with no value.
1144     {
1145         json configFile = i2cCompareBitFile;
1146         configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase("value");
1147         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1148                             "'value' is a required property");
1149     }
1150     // Invalid: test i2c_compare_bit with register wrong type.
1151     {
1152         json configFile = i2cCompareBitFile;
1153         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = 1;
1154         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1155                             "1 is not of type 'string'");
1156     }
1157     // Invalid: test i2c_compare_bit with register wrong format.
1158     {
1159         json configFile = i2cCompareBitFile;
1160         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] =
1161             "0xA00";
1162         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1163                             "'0xA00' does not match '^0x[0-9A-Fa-f]{2}$'");
1164     }
1165     // Invalid: test i2c_compare_bit with position wrong type.
1166     {
1167         json configFile = i2cCompareBitFile;
1168         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] =
1169             3.1;
1170         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1171                             "3.1 is not of type 'integer'");
1172     }
1173     // Invalid: test i2c_compare_bit with position greater than 7.
1174     {
1175         json configFile = i2cCompareBitFile;
1176         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 8;
1177         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1178                             "8 is greater than the maximum of 7");
1179     }
1180     // Invalid: test i2c_compare_bit with position less than 0.
1181     {
1182         json configFile = i2cCompareBitFile;
1183         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] =
1184             -1;
1185         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1186                             "-1 is less than the minimum of 0");
1187     }
1188     // Invalid: test i2c_compare_bit with value wrong type.
1189     {
1190         json configFile = i2cCompareBitFile;
1191         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = "1";
1192         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1193                             "'1' is not of type 'integer'");
1194     }
1195     // Invalid: test i2c_compare_bit with value greater than 1.
1196     {
1197         json configFile = i2cCompareBitFile;
1198         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 2;
1199         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1200                             "2 is greater than the maximum of 1");
1201     }
1202     // Invalid: test i2c_compare_bit with value less than 0.
1203     {
1204         json configFile = i2cCompareBitFile;
1205         configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = -1;
1206         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1207                             "-1 is less than the minimum of 0");
1208     }
1209 }
1210 TEST(ValidateRegulatorsConfigTest, I2CCompareByte)
1211 {
1212     json i2cCompareByteFile = validConfigFile;
1213     i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]
1214                       ["register"] = "0x82";
1215     i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
1216         "0x40";
1217     i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] =
1218         "0x7F";
1219     // Valid: test i2c_compare_byte with all properties.
1220     {
1221         json configFile = i2cCompareByteFile;
1222         EXPECT_JSON_VALID(configFile);
1223     }
1224     // Valid: test i2c_compare_byte with all required properties.
1225     {
1226         json configFile = i2cCompareByteFile;
1227         configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("mask");
1228         EXPECT_JSON_VALID(configFile);
1229     }
1230     // Invalid: test i2c_compare_byte with no register.
1231     {
1232         json configFile = i2cCompareByteFile;
1233         configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase(
1234             "register");
1235         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1236                             "'register' is a required property");
1237     }
1238     // Invalid: test i2c_compare_byte with no value.
1239     {
1240         json configFile = i2cCompareByteFile;
1241         configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("value");
1242         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1243                             "'value' is a required property");
1244     }
1245     // Invalid: test i2c_compare_byte with property register wrong type.
1246     {
1247         json configFile = i2cCompareByteFile;
1248         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
1249             1;
1250         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1251                             "1 is not of type 'string'");
1252     }
1253     // Invalid: test i2c_compare_byte with property value wrong type.
1254     {
1255         json configFile = i2cCompareByteFile;
1256         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 1;
1257         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1258                             "1 is not of type 'string'");
1259     }
1260     // Invalid: test i2c_compare_byte with property mask wrong type.
1261     {
1262         json configFile = i2cCompareByteFile;
1263         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 1;
1264         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1265                             "1 is not of type 'string'");
1266     }
1267     // Invalid: test i2c_compare_byte with property register more than 2 hex
1268     // digits.
1269     {
1270         json configFile = i2cCompareByteFile;
1271         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
1272             "0x820";
1273         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1274                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1275     }
1276     // Invalid: test i2c_compare_byte with property value more than 2 hex
1277     // digits.
1278     {
1279         json configFile = i2cCompareByteFile;
1280         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
1281             "0x820";
1282         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1283                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1284     }
1285     // Invalid: test i2c_compare_byte with property mask more than 2 hex digits.
1286     {
1287         json configFile = i2cCompareByteFile;
1288         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] =
1289             "0x820";
1290         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1291                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1292     }
1293     // Invalid: test i2c_compare_byte with property register less than 2 hex
1294     // digits.
1295     {
1296         json configFile = i2cCompareByteFile;
1297         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
1298             "0x8";
1299         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1300                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1301     }
1302     // Invalid: test i2c_compare_byte with property value less than 2 hex
1303     // digits.
1304     {
1305         json configFile = i2cCompareByteFile;
1306         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
1307             "0x8";
1308         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1309                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1310     }
1311     // Invalid: test i2c_compare_byte with property mask less than 2 hex digits.
1312     {
1313         json configFile = i2cCompareByteFile;
1314         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] =
1315             "0x8";
1316         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1317                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1318     }
1319     // Invalid: test i2c_compare_byte with property register no leading prefix.
1320     {
1321         json configFile = i2cCompareByteFile;
1322         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
1323             "82";
1324         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1325                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1326     }
1327     // Invalid: test i2c_compare_byte with property value no leading prefix.
1328     {
1329         json configFile = i2cCompareByteFile;
1330         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
1331             "82";
1332         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1333                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1334     }
1335     // Invalid: test i2c_compare_byte with property mask no leading prefix.
1336     {
1337         json configFile = i2cCompareByteFile;
1338         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "82";
1339         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1340                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1341     }
1342     // Invalid: test i2c_compare_byte with property register invalid hex digit.
1343     {
1344         json configFile = i2cCompareByteFile;
1345         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] =
1346             "0xG1";
1347         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1348                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1349     }
1350     // Invalid: test i2c_compare_byte with property value invalid hex digit.
1351     {
1352         json configFile = i2cCompareByteFile;
1353         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] =
1354             "0xG1";
1355         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1356                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1357     }
1358     // Invalid: test i2c_compare_byte with property mask invalid hex digit.
1359     {
1360         json configFile = i2cCompareByteFile;
1361         configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] =
1362             "0xG1";
1363         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1364                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1365     }
1366 }
1367 TEST(ValidateRegulatorsConfigTest, I2CCompareBytes)
1368 {
1369     json i2cCompareBytesFile = validConfigFile;
1370     i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"]
1371                        ["register"] = "0x82";
1372     i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"]
1373                        ["values"] = {"0x02", "0x73"};
1374     i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"]
1375                        ["masks"] = {"0x7F", "0x7F"};
1376     // Valid: test i2c_compare_bytes.
1377     {
1378         json configFile = i2cCompareBytesFile;
1379         EXPECT_JSON_VALID(configFile);
1380     }
1381     // Valid: test i2c_compare_bytes with all required properties.
1382     {
1383         json configFile = i2cCompareBytesFile;
1384         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase(
1385             "masks");
1386         EXPECT_JSON_VALID(configFile);
1387     }
1388     // Invalid: test i2c_compare_bytes with no register.
1389     {
1390         json configFile = i2cCompareBytesFile;
1391         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase(
1392             "register");
1393         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1394                             "'register' is a required property");
1395     }
1396     // Invalid: test i2c_compare_bytes with no values.
1397     {
1398         json configFile = i2cCompareBytesFile;
1399         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase(
1400             "values");
1401         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1402                             "'values' is a required property");
1403     }
1404     // Invalid: test i2c_compare_bytes with property values as empty array.
1405     {
1406         json configFile = i2cCompareBytesFile;
1407         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] =
1408             json::array();
1409         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1410                             "[] is too short");
1411     }
1412     // Invalid: test i2c_compare_bytes with property masks as empty array.
1413     {
1414         json configFile = i2cCompareBytesFile;
1415         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] =
1416             json::array();
1417         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1418                             "[] is too short");
1419     }
1420     // Invalid: test i2c_compare_bytes with property register wrong type.
1421     {
1422         json configFile = i2cCompareBytesFile;
1423         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
1424             1;
1425         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1426                             "1 is not of type 'string'");
1427     }
1428     // Invalid: test i2c_compare_bytes with property values wrong type.
1429     {
1430         json configFile = i2cCompareBytesFile;
1431         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = 1;
1432         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1433                             "1 is not of type 'array'");
1434     }
1435     // Invalid: test i2c_compare_bytes with property masks wrong type.
1436     {
1437         json configFile = i2cCompareBytesFile;
1438         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = 1;
1439         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1440                             "1 is not of type 'array'");
1441     }
1442     // Invalid: test i2c_compare_bytes with property register more than 2 hex
1443     // digits.
1444     {
1445         json configFile = i2cCompareBytesFile;
1446         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
1447             "0x820";
1448         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1449                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1450     }
1451     // Invalid: test i2c_compare_bytes with property values more than 2 hex
1452     // digits.
1453     {
1454         json configFile = i2cCompareBytesFile;
1455         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] =
1456             "0x820";
1457         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1458                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1459     }
1460     // Invalid: test i2c_compare_bytes with property masks more than 2 hex
1461     // digits.
1462     {
1463         json configFile = i2cCompareBytesFile;
1464         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] =
1465             "0x820";
1466         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1467                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1468     }
1469     // Invalid: test i2c_compare_bytes with property register less than 2 hex
1470     // digits.
1471     {
1472         json configFile = i2cCompareBytesFile;
1473         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
1474             "0x8";
1475         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1476                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1477     }
1478     // Invalid: test i2c_compare_bytes with property values less than 2 hex
1479     // digits.
1480     {
1481         json configFile = i2cCompareBytesFile;
1482         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] =
1483             "0x8";
1484         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1485                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1486     }
1487     // Invalid: test i2c_compare_bytes with property masks less than 2 hex
1488     // digits.
1489     {
1490         json configFile = i2cCompareBytesFile;
1491         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] =
1492             "0x8";
1493         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1494                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1495     }
1496     // Invalid: test i2c_compare_bytes with property register no leading prefix.
1497     {
1498         json configFile = i2cCompareBytesFile;
1499         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
1500             "82";
1501         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1502                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1503     }
1504     // Invalid: test i2c_compare_bytes with property values no leading prefix.
1505     {
1506         json configFile = i2cCompareBytesFile;
1507         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] =
1508             "82";
1509         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1510                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1511     }
1512     // Invalid: test i2c_compare_bytes with property masks no leading prefix.
1513     {
1514         json configFile = i2cCompareBytesFile;
1515         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] =
1516             "82";
1517         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1518                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1519     }
1520     // Invalid: test i2c_compare_bytes with property register invalid hex digit.
1521     {
1522         json configFile = i2cCompareBytesFile;
1523         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
1524             "0xG1";
1525         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1526                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1527     }
1528     // Invalid: test i2c_compare_bytes with property values invalid hex digit.
1529     {
1530         json configFile = i2cCompareBytesFile;
1531         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] =
1532             "0xG1";
1533         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1534                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1535     }
1536     // Invalid: test i2c_compare_bytes with property masks invalid hex digit.
1537     {
1538         json configFile = i2cCompareBytesFile;
1539         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] =
1540             "0xG1";
1541         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1542                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1543     }
1544 }
1545 TEST(ValidateRegulatorsConfigTest, I2CInterface)
1546 {
1547     // Valid: test i2c_interface.
1548     {
1549         json configFile = validConfigFile;
1550         EXPECT_JSON_VALID(configFile);
1551     }
1552     // Invalid: testi2c_interface with no bus.
1553     {
1554         json configFile = validConfigFile;
1555         configFile["chassis"][0]["devices"][0]["i2c_interface"].erase("bus");
1556         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1557                             "'bus' is a required property");
1558     }
1559     // Invalid: test i2c_interface with no address.
1560     {
1561         json configFile = validConfigFile;
1562         configFile["chassis"][0]["devices"][0]["i2c_interface"].erase(
1563             "address");
1564         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1565                             "'address' is a required property");
1566     }
1567     // Invalid: test i2c_interface with property bus wrong type.
1568     {
1569         json configFile = validConfigFile;
1570         configFile["chassis"][0]["devices"][0]["i2c_interface"]["bus"] = true;
1571         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1572                             "True is not of type 'integer'");
1573     }
1574     // Invalid: test i2c_interface with property address wrong
1575     // type.
1576     {
1577         json configFile = validConfigFile;
1578         configFile["chassis"][0]["devices"][0]["i2c_interface"]["address"] =
1579             true;
1580         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1581                             "True is not of type 'string'");
1582     }
1583     // Invalid: test i2c_interface with property bus less than
1584     // 0.
1585     {
1586         json configFile = validConfigFile;
1587         configFile["chassis"][0]["devices"][0]["i2c_interface"]["bus"] = -1;
1588         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1589                             "-1 is less than the minimum of 0");
1590     }
1591     // Invalid: test i2c_interface with property address wrong
1592     // format.
1593     {
1594         json configFile = validConfigFile;
1595         configFile["chassis"][0]["devices"][0]["i2c_interface"]["address"] =
1596             "0x700";
1597         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1598                             "'0x700' does not match '^0x[0-9A-Fa-f]{2}$'");
1599     }
1600 }
1601 TEST(ValidateRegulatorsConfigTest, I2CWriteBit)
1602 {
1603     json i2cWriteBitFile = validConfigFile;
1604     i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] =
1605         "0xA0";
1606     i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3;
1607     i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 1;
1608     // Valid: test rule actions i2c_write_bit.
1609     {
1610         json configFile = i2cWriteBitFile;
1611         EXPECT_JSON_VALID(configFile);
1612     }
1613     // Invalid: test i2c_write_bit with no register.
1614     {
1615         json configFile = i2cWriteBitFile;
1616         configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("register");
1617         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1618                             "'register' is a required property");
1619     }
1620     // Invalid: test i2c_write_bit with no position.
1621     {
1622         json configFile = i2cWriteBitFile;
1623         configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("position");
1624         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1625                             "'position' is a required property");
1626     }
1627     // Invalid: test i2c_write_bit with no value.
1628     {
1629         json configFile = i2cWriteBitFile;
1630         configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("value");
1631         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1632                             "'value' is a required property");
1633     }
1634     // Invalid: test i2c_write_bit with register wrong type.
1635     {
1636         json configFile = i2cWriteBitFile;
1637         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] = 1;
1638         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1639                             "1 is not of type 'string'");
1640     }
1641     // Invalid: test i2c_write_bit with register wrong format.
1642     {
1643         json configFile = i2cWriteBitFile;
1644         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] =
1645             "0xA00";
1646         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1647                             "'0xA00' does not match '^0x[0-9A-Fa-f]{2}$'");
1648     }
1649     // Invalid: test i2c_write_bit with position wrong type.
1650     {
1651         json configFile = i2cWriteBitFile;
1652         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3.1;
1653         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1654                             "3.1 is not of type 'integer'");
1655     }
1656     // Invalid: test i2c_write_bit with position greater than 7.
1657     {
1658         json configFile = i2cWriteBitFile;
1659         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 8;
1660         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1661                             "8 is greater than the maximum of 7");
1662     }
1663     // Invalid: test i2c_write_bit with position less than 0.
1664     {
1665         json configFile = i2cWriteBitFile;
1666         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = -1;
1667         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1668                             "-1 is less than the minimum of 0");
1669     }
1670     // Invalid: test i2c_write_bit with value wrong type.
1671     {
1672         json configFile = i2cWriteBitFile;
1673         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = "1";
1674         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1675                             "'1' is not of type 'integer'");
1676     }
1677     // Invalid: test i2c_write_bit with value greater than 1.
1678     {
1679         json configFile = i2cWriteBitFile;
1680         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 2;
1681         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1682                             "2 is greater than the maximum of 1");
1683     }
1684     // Invalid: test i2c_write_bit with value less than 0.
1685     {
1686         json configFile = i2cWriteBitFile;
1687         configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = -1;
1688         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1689                             "-1 is less than the minimum of 0");
1690     }
1691 }
1692 TEST(ValidateRegulatorsConfigTest, I2CWriteByte)
1693 {
1694     json i2cWriteByteFile = validConfigFile;
1695     i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
1696         "0x82";
1697     i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] =
1698         "0x40";
1699     i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] =
1700         "0x7F";
1701     // Valid: test i2c_write_byte with all properties.
1702     {
1703         json configFile = i2cWriteByteFile;
1704         EXPECT_JSON_VALID(configFile);
1705     }
1706     // Valid: test i2c_write_byte with all required properties.
1707     {
1708         json configFile = i2cWriteByteFile;
1709         configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase("mask");
1710         EXPECT_JSON_VALID(configFile);
1711     }
1712     // Invalid: test i2c_write_byte with no register.
1713     {
1714         json configFile = i2cWriteByteFile;
1715         configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase(
1716             "register");
1717         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1718                             "'register' is a required property");
1719     }
1720     // Invalid: test i2c_write_byte with no value.
1721     {
1722         json configFile = i2cWriteByteFile;
1723         configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase("value");
1724         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1725                             "'value' is a required property");
1726     }
1727     // Invalid: test i2c_write_byte with property register wrong type.
1728     {
1729         json configFile = i2cWriteByteFile;
1730         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = 1;
1731         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1732                             "1 is not of type 'string'");
1733     }
1734     // Invalid: test i2c_write_byte with property value wrong type.
1735     {
1736         json configFile = i2cWriteByteFile;
1737         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = 1;
1738         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1739                             "1 is not of type 'string'");
1740     }
1741     // Invalid: test i2c_write_byte with property mask wrong type.
1742     {
1743         json configFile = i2cWriteByteFile;
1744         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = 1;
1745         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1746                             "1 is not of type 'string'");
1747     }
1748     // Invalid: test i2c_write_byte with property register more than 2 hex
1749     // digits.
1750     {
1751         json configFile = i2cWriteByteFile;
1752         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
1753             "0x820";
1754         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1755                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1756     }
1757     // Invalid: test i2c_write_byte with property value more than 2 hex
1758     // digits.
1759     {
1760         json configFile = i2cWriteByteFile;
1761         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] =
1762             "0x820";
1763         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1764                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1765     }
1766     // Invalid: test i2c_write_byte with property mask more than 2 hex digits.
1767     {
1768         json configFile = i2cWriteByteFile;
1769         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] =
1770             "0x820";
1771         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1772                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1773     }
1774     // Invalid: test i2c_write_byte with property register less than 2 hex
1775     // digits.
1776     {
1777         json configFile = i2cWriteByteFile;
1778         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
1779             "0x8";
1780         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1781                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1782     }
1783     // Invalid: test i2c_write_byte with property value less than 2 hex
1784     // digits.
1785     {
1786         json configFile = i2cWriteByteFile;
1787         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0x8";
1788         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1789                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1790     }
1791     // Invalid: test i2c_write_byte with property mask less than 2 hex digits.
1792     {
1793         json configFile = i2cWriteByteFile;
1794         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x8";
1795         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1796                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1797     }
1798     // Invalid: test i2c_write_byte with property register no leading prefix.
1799     {
1800         json configFile = i2cWriteByteFile;
1801         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
1802             "82";
1803         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1804                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1805     }
1806     // Invalid: test i2c_write_byte with property value no leading prefix.
1807     {
1808         json configFile = i2cWriteByteFile;
1809         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "82";
1810         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1811                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1812     }
1813     // Invalid: test i2c_write_byte with property mask no leading prefix.
1814     {
1815         json configFile = i2cWriteByteFile;
1816         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "82";
1817         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1818                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1819     }
1820     // Invalid: test i2c_write_byte with property register invalid hex digit.
1821     {
1822         json configFile = i2cWriteByteFile;
1823         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] =
1824             "0xG1";
1825         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1826                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1827     }
1828     // Invalid: test i2c_write_byte with property value invalid hex digit.
1829     {
1830         json configFile = i2cWriteByteFile;
1831         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] =
1832             "0xG1";
1833         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1834                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1835     }
1836     // Invalid: test i2c_write_byte with property mask invalid hex digit.
1837     {
1838         json configFile = i2cWriteByteFile;
1839         configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0xG1";
1840         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1841                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
1842     }
1843 }
1844 TEST(ValidateRegulatorsConfigTest, I2CWriteBytes)
1845 {
1846     json i2cWriteBytesFile = validConfigFile;
1847     i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
1848         "0x82";
1849     i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = {
1850         "0x02", "0x73"};
1851     i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = {
1852         "0x7F", "0x7F"};
1853     // Valid: test i2c_write_bytes.
1854     {
1855         json configFile = i2cWriteBytesFile;
1856         EXPECT_JSON_VALID(configFile);
1857     }
1858     // Valid: test i2c_write_bytes with all required properties.
1859     {
1860         json configFile = i2cWriteBytesFile;
1861         configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase("masks");
1862         EXPECT_JSON_VALID(configFile);
1863     }
1864     // Invalid: test i2c_write_bytes with no register.
1865     {
1866         json configFile = i2cWriteBytesFile;
1867         configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase(
1868             "register");
1869         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1870                             "'register' is a required property");
1871     }
1872     // Invalid: test i2c_write_bytes with no values.
1873     {
1874         json configFile = i2cWriteBytesFile;
1875         configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase("values");
1876         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1877                             "'values' is a required property");
1878     }
1879     // Invalid: test i2c_write_bytes with property values as empty array.
1880     {
1881         json configFile = i2cWriteBytesFile;
1882         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] =
1883             json::array();
1884         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1885                             "[] is too short");
1886     }
1887     // Invalid: test i2c_write_bytes with property masks as empty array.
1888     {
1889         json configFile = i2cWriteBytesFile;
1890         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] =
1891             json::array();
1892         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1893                             "[] is too short");
1894     }
1895     // Invalid: test i2c_write_bytes with property register wrong type.
1896     {
1897         json configFile = i2cWriteBytesFile;
1898         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = 1;
1899         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1900                             "1 is not of type 'string'");
1901     }
1902     // Invalid: test i2c_write_bytes with property values wrong type.
1903     {
1904         json configFile = i2cWriteBytesFile;
1905         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = 1;
1906         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1907                             "1 is not of type 'array'");
1908     }
1909     // Invalid: test i2c_write_bytes with property masks wrong type.
1910     {
1911         json configFile = i2cWriteBytesFile;
1912         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = 1;
1913         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1914                             "1 is not of type 'array'");
1915     }
1916     // Invalid: test i2c_write_bytes with property register more than 2 hex
1917     // digits.
1918     {
1919         json configFile = i2cWriteBytesFile;
1920         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
1921             "0x820";
1922         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1923                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1924     }
1925     // Invalid: test i2c_write_bytes with property values more than 2 hex
1926     // digits.
1927     {
1928         json configFile = i2cWriteBytesFile;
1929         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] =
1930             "0x820";
1931         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1932                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1933     }
1934     // Invalid: test i2c_write_bytes with property masks more than 2 hex
1935     // digits.
1936     {
1937         json configFile = i2cWriteBytesFile;
1938         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] =
1939             "0x820";
1940         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1941                             "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'");
1942     }
1943     // Invalid: test i2c_write_bytes with property register less than 2 hex
1944     // digits.
1945     {
1946         json configFile = i2cWriteBytesFile;
1947         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
1948             "0x8";
1949         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1950                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1951     }
1952     // Invalid: test i2c_write_bytes with property values less than 2 hex
1953     // digits.
1954     {
1955         json configFile = i2cWriteBytesFile;
1956         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] =
1957             "0x8";
1958         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1959                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1960     }
1961     // Invalid: test i2c_write_bytes with property masks less than 2 hex
1962     // digits.
1963     {
1964         json configFile = i2cWriteBytesFile;
1965         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] =
1966             "0x8";
1967         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1968                             "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'");
1969     }
1970     // Invalid: test i2c_write_bytes with property register no leading prefix.
1971     {
1972         json configFile = i2cWriteBytesFile;
1973         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
1974             "82";
1975         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1976                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1977     }
1978     // Invalid: test i2c_write_bytes with property values no leading prefix.
1979     {
1980         json configFile = i2cWriteBytesFile;
1981         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] =
1982             "82";
1983         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1984                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1985     }
1986     // Invalid: test i2c_write_bytes with property masks no leading prefix.
1987     {
1988         json configFile = i2cWriteBytesFile;
1989         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] =
1990             "82";
1991         EXPECT_JSON_INVALID(configFile, "Validation failed.",
1992                             "'82' does not match '^0x[0-9A-Fa-f]{2}$'");
1993     }
1994     // Invalid: test i2c_write_bytes with property register invalid hex digit.
1995     {
1996         json configFile = i2cWriteBytesFile;
1997         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
1998             "0xG1";
1999         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2000                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
2001     }
2002     // Invalid: test i2c_write_bytes with property values invalid hex digit.
2003     {
2004         json configFile = i2cWriteBytesFile;
2005         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] =
2006             "0xG1";
2007         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2008                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
2009     }
2010     // Invalid: test i2c_write_bytes with property masks invalid hex digit.
2011     {
2012         json configFile = i2cWriteBytesFile;
2013         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] =
2014             "0xG1";
2015         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2016                             "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'");
2017     }
2018 }
2019 TEST(ValidateRegulatorsConfigTest, If)
2020 {
2021     json ifFile = validConfigFile;
2022     ifFile["rules"][2]["actions"][0]["if"]["condition"]["run_rule"] =
2023         "set_voltage_rule";
2024     ifFile["rules"][2]["actions"][0]["if"]["then"][0]["run_rule"] =
2025         "read_sensors_rule";
2026     ifFile["rules"][2]["actions"][0]["if"]["else"][0]["run_rule"] =
2027         "read_sensors_rule";
2028     ifFile["rules"][2]["id"] = "rule_if";
2029     // Valid: test if.
2030     {
2031         json configFile = ifFile;
2032         EXPECT_JSON_VALID(configFile);
2033     }
2034     // Valid: test if with required properties.
2035     {
2036         json configFile = ifFile;
2037         configFile["rules"][2]["actions"][0]["if"].erase("else");
2038         EXPECT_JSON_VALID(configFile);
2039     }
2040     // Invalid: test if with no property condition.
2041     {
2042         json configFile = ifFile;
2043         configFile["rules"][2]["actions"][0]["if"].erase("condition");
2044         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2045                             "'condition' is a required property");
2046     }
2047     // Invalid: test if with no property then.
2048     {
2049         json configFile = ifFile;
2050         configFile["rules"][2]["actions"][0]["if"].erase("then");
2051         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2052                             "'then' is a required property");
2053     }
2054     // Invalid: test if with property then empty array.
2055     {
2056         json configFile = ifFile;
2057         configFile["rules"][2]["actions"][0]["if"]["then"] = json::array();
2058         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2059                             "[] is too short");
2060     }
2061     // Invalid: test if with property else empty array.
2062     {
2063         json configFile = ifFile;
2064         configFile["rules"][2]["actions"][0]["if"]["else"] = json::array();
2065         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2066                             "[] is too short");
2067     }
2068     // Invalid: test if with property condition wrong type.
2069     {
2070         json configFile = ifFile;
2071         configFile["rules"][2]["actions"][0]["if"]["condition"] = 1;
2072         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2073                             "1 is not of type 'object'");
2074     }
2075     // Invalid: test if with property then wrong type.
2076     {
2077         json configFile = ifFile;
2078         configFile["rules"][2]["actions"][0]["if"]["then"] = 1;
2079         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2080                             "1 is not of type 'array'");
2081     }
2082     // Invalid: test if with property else wrong type.
2083     {
2084         json configFile = ifFile;
2085         configFile["rules"][2]["actions"][0]["if"]["else"] = 1;
2086         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2087                             "1 is not of type 'array'");
2088     }
2089 }
2090 TEST(ValidateRegulatorsConfigTest, Not)
2091 {
2092     json notFile = validConfigFile;
2093     notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["register"] =
2094         "0xA0";
2095     notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["value"] =
2096         "0xFF";
2097     // Valid: test not.
2098     {
2099         json configFile = notFile;
2100         EXPECT_JSON_VALID(configFile);
2101     }
2102     // Invalid: test not with wrong type.
2103     {
2104         json configFile = notFile;
2105         configFile["rules"][0]["actions"][1]["not"] = 1;
2106         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2107                             "1 is not of type 'object'");
2108     }
2109 }
2110 TEST(ValidateRegulatorsConfigTest, Or)
2111 {
2112     json orFile = validConfigFile;
2113     orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["register"] =
2114         "0xA0";
2115     orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["value"] =
2116         "0x00";
2117     orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["register"] =
2118         "0xA1";
2119     orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["value"] =
2120         "0x00";
2121     // Valid: test or.
2122     {
2123         json configFile = orFile;
2124         EXPECT_JSON_VALID(configFile);
2125     }
2126     // Invalid: test or with empty array.
2127     {
2128         json configFile = orFile;
2129         configFile["rules"][0]["actions"][1]["or"] = json::array();
2130         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2131                             "[] is too short");
2132     }
2133     // Invalid: test or with wrong type.
2134     {
2135         json configFile = orFile;
2136         configFile["rules"][0]["actions"][1]["or"] = 1;
2137         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2138                             "1 is not of type 'array'");
2139     }
2140 }
2141 TEST(ValidateRegulatorsConfigTest, PmbusReadSensor)
2142 {
2143     json pmbusReadSensorFile = validConfigFile;
2144     pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] =
2145         "vout";
2146     pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"]
2147                        ["command"] = "0x8B";
2148     pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"]
2149                        ["format"] = "linear_16";
2150     pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"]
2151                        ["exponent"] = -8;
2152     // Valid: test pmbus_read_sensor.
2153     {
2154         json configFile = pmbusReadSensorFile;
2155         EXPECT_JSON_VALID(configFile);
2156     }
2157     // Valid: test pmbus_read_sensor with required properties.
2158     {
2159         json configFile = pmbusReadSensorFile;
2160         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase(
2161             "exponent");
2162         EXPECT_JSON_VALID(configFile);
2163     }
2164     // Invalid: test pmbus_read_sensor with no type.
2165     {
2166         json configFile = pmbusReadSensorFile;
2167         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase("type");
2168         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2169                             "'type' is a required property");
2170     }
2171     // Invalid: test pmbus_read_sensor with no command.
2172     {
2173         json configFile = pmbusReadSensorFile;
2174         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase(
2175             "command");
2176         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2177                             "'command' is a required property");
2178     }
2179     // Invalid: test pmbus_read_sensor with no format.
2180     {
2181         json configFile = pmbusReadSensorFile;
2182         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase(
2183             "format");
2184         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2185                             "'format' is a required property");
2186     }
2187     // Invalid: test pmbus_read_sensor with property type wrong type.
2188     {
2189         json configFile = pmbusReadSensorFile;
2190         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] =
2191             true;
2192         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2193                             "True is not of type 'string'");
2194     }
2195     // Invalid: test pmbus_read_sensor with property command wrong type.
2196     {
2197         json configFile = pmbusReadSensorFile;
2198         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["command"] =
2199             true;
2200         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2201                             "True is not of type 'string'");
2202     }
2203     // Invalid: test pmbus_read_sensor with property format wrong type.
2204     {
2205         json configFile = pmbusReadSensorFile;
2206         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["format"] =
2207             true;
2208         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2209                             "True is not of type 'string'");
2210     }
2211     // Invalid: test pmbus_read_sensor with property exponent wrong type.
2212     {
2213         json configFile = pmbusReadSensorFile;
2214         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["exponent"] =
2215             true;
2216         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2217                             "True is not of type 'integer'");
2218     }
2219     // Invalid: test pmbus_read_sensor with property type wrong format.
2220     {
2221         json configFile = pmbusReadSensorFile;
2222         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] =
2223             "foo";
2224         EXPECT_JSON_INVALID(
2225             configFile, "Validation failed.",
2226             "'foo' is not one of ['iout', 'iout_peak', 'iout_valley', "
2227             "'pout', 'temperature', 'temperature_peak', 'vout', "
2228             "'vout_peak', 'vout_valley']");
2229     }
2230     // Invalid: test pmbus_read_sensor with property command wrong format.
2231     {
2232         json configFile = pmbusReadSensorFile;
2233         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["command"] =
2234             "0x8B0";
2235         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2236                             "'0x8B0' does not match '^0x[0-9a-fA-F]{2}$'");
2237     }
2238     // Invalid: test pmbus_read_sensor with property format wrong format.
2239     {
2240         json configFile = pmbusReadSensorFile;
2241         configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["format"] =
2242             "foo";
2243         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2244                             "'foo' is not one of ['linear_11', 'linear_16']");
2245     }
2246 }
2247 TEST(ValidateRegulatorsConfigTest, PmbusWriteVoutCommand)
2248 {
2249     json pmbusWriteVoutCommandFile = validConfigFile;
2250     pmbusWriteVoutCommandFile["rules"][0]["actions"][1]
2251                              ["pmbus_write_vout_command"]["volts"] = 1.03;
2252     pmbusWriteVoutCommandFile["rules"][0]["actions"][1]
2253                              ["pmbus_write_vout_command"]["format"] = "linear";
2254     pmbusWriteVoutCommandFile["rules"][0]["actions"][1]
2255                              ["pmbus_write_vout_command"]["exponent"] = -8;
2256     pmbusWriteVoutCommandFile["rules"][0]["actions"][1]
2257                              ["pmbus_write_vout_command"]["is_verified"] = true;
2258     // Valid: test pmbus_write_vout_command.
2259     {
2260         json configFile = pmbusWriteVoutCommandFile;
2261         EXPECT_JSON_VALID(configFile);
2262     }
2263     // Valid: test pmbus_write_vout_command with required properties.
2264     {
2265         json configFile = pmbusWriteVoutCommandFile;
2266         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase(
2267             "volts");
2268         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase(
2269             "exponent");
2270         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase(
2271             "is_verified");
2272         EXPECT_JSON_VALID(configFile);
2273     }
2274     // Invalid: test pmbus_write_vout_command with no format.
2275     {
2276         json configFile = pmbusWriteVoutCommandFile;
2277         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase(
2278             "format");
2279         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2280                             "'format' is a required property");
2281     }
2282     // Invalid: test pmbus_write_vout_command with property volts wrong type.
2283     {
2284         json configFile = pmbusWriteVoutCommandFile;
2285         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"]
2286                   ["volts"] = true;
2287         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2288                             "True is not of type 'number'");
2289     }
2290     // Invalid: test pmbus_write_vout_command with property format wrong type.
2291     {
2292         json configFile = pmbusWriteVoutCommandFile;
2293         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"]
2294                   ["format"] = true;
2295         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2296                             "True is not of type 'string'");
2297     }
2298     // Invalid: test pmbus_write_vout_command with property exponent wrong type.
2299     {
2300         json configFile = pmbusWriteVoutCommandFile;
2301         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"]
2302                   ["exponent"] = 1.3;
2303         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2304                             "1.3 is not of type 'integer'");
2305     }
2306     // Invalid: test pmbus_write_vout_command with property is_verified wrong
2307     // type.
2308     {
2309         json configFile = pmbusWriteVoutCommandFile;
2310         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"]
2311                   ["is_verified"] = 1;
2312         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2313                             "1 is not of type 'boolean'");
2314     }
2315     // Invalid: test pmbus_write_vout_command with property format wrong format.
2316     {
2317         json configFile = pmbusWriteVoutCommandFile;
2318         configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"]
2319                   ["format"] = "foo";
2320         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2321                             "'foo' is not one of ['linear']");
2322     }
2323 }
2324 TEST(ValidateRegulatorsConfigTest, PresenceDetection)
2325 {
2326     json presenceDetectionFile = validConfigFile;
2327     presenceDetectionFile
2328         ["chassis"][0]["devices"][0]["presence_detection"]["comments"][0] =
2329             "Regulator is only present on the FooBar backplane";
2330     presenceDetectionFile["chassis"][0]["devices"][0]["presence_detection"]
2331                          ["rule_id"] = "set_voltage_rule";
2332     // Valid: test presence_detection with only property rule_id.
2333     {
2334         json configFile = presenceDetectionFile;
2335         EXPECT_JSON_VALID(configFile);
2336     }
2337     // Valid: test presence_detection with only property actions.
2338     {
2339         json configFile = presenceDetectionFile;
2340         configFile["chassis"][0]["devices"][0]["presence_detection"].erase(
2341             "rule_id");
2342         configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"]
2343                   [0]["compare_presence"]["fru"] =
2344                       "system/chassis/motherboard/cpu3";
2345         configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"]
2346                   [0]["compare_presence"]["value"] = true;
2347         configFile["chassis"][0]["devices"][0]["presence_detection"].erase(
2348             "comments");
2349         EXPECT_JSON_VALID(configFile);
2350     }
2351     // Invalid: test presence_detection with both property rule_id and actions.
2352     {
2353         json configFile = presenceDetectionFile;
2354         configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"]
2355                   [0]["compare_presence"]["fru"] =
2356                       "system/chassis/motherboard/cpu3";
2357         configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"]
2358                   [0]["compare_presence"]["value"] = true;
2359         EXPECT_JSON_INVALID(
2360             configFile, "Validation failed.",
2361             "{'actions': [{'compare_presence': {'fru': "
2362             "'system/chassis/motherboard/cpu3', 'value': True}}], 'comments': "
2363             "['Regulator is only present on the FooBar backplane'], 'rule_id': "
2364             "'set_voltage_rule'} is valid under each of {'required': "
2365             "['actions']}, {'required': ['rule_id']}");
2366     }
2367     // Invalid: test presence_detection with no rule_id and actions.
2368     {
2369         json configFile = presenceDetectionFile;
2370         configFile["chassis"][0]["devices"][0]["presence_detection"].erase(
2371             "rule_id");
2372         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2373                             "'rule_id' is a required property");
2374     }
2375     // Invalid: test presence_detection with property comments wrong type.
2376     {
2377         json configFile = presenceDetectionFile;
2378         configFile["chassis"][0]["devices"][0]["presence_detection"]
2379                   ["comments"] = true;
2380         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2381                             "True is not of type 'array'");
2382     }
2383     // Invalid: test presence_detection with property rule_id wrong type.
2384     {
2385         json configFile = presenceDetectionFile;
2386         configFile["chassis"][0]["devices"][0]["presence_detection"]
2387                   ["rule_id"] = true;
2388         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2389                             "True is not of type 'string'");
2390     }
2391     // Invalid: test presence_detection with property actions wrong type.
2392     {
2393         json configFile = presenceDetectionFile;
2394         configFile["chassis"][0]["devices"][0]["presence_detection"].erase(
2395             "rule_id");
2396         configFile["chassis"][0]["devices"][0]["presence_detection"]
2397                   ["actions"] = true;
2398         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2399                             "True is not of type 'array'");
2400     }
2401     // Invalid: test presence_detection with property rule_id wrong format.
2402     {
2403         json configFile = presenceDetectionFile;
2404         configFile["chassis"][0]["devices"][0]["presence_detection"]
2405                   ["rule_id"] = "id@";
2406         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2407                             "'id@' does not match '^[A-Za-z0-9_]+$'");
2408     }
2409     // Invalid: test presence_detection with property comments empty array.
2410     {
2411         json configFile = presenceDetectionFile;
2412         configFile["chassis"][0]["devices"][0]["presence_detection"]
2413                   ["comments"] = json::array();
2414         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2415                             "[] is too short");
2416     }
2417     // Invalid: test presence_detection with property actions empty array.
2418     {
2419         json configFile = presenceDetectionFile;
2420         configFile["chassis"][0]["devices"][0]["presence_detection"].erase(
2421             "rule_id");
2422         configFile["chassis"][0]["devices"][0]["presence_detection"]
2423                   ["actions"] = json::array();
2424         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2425                             "[] is too short");
2426     }
2427 }
2428 TEST(ValidateRegulatorsConfigTest, Rail)
2429 {
2430     // Valid: test rail.
2431     {
2432         json configFile = validConfigFile;
2433         EXPECT_JSON_VALID(configFile);
2434     }
2435     // Valid: test rail with required properties.
2436     {
2437         json configFile = validConfigFile;
2438         configFile["chassis"][0]["devices"][0]["rails"][0].erase("comments");
2439         configFile["chassis"][0]["devices"][0]["rails"][0].erase(
2440             "configuration");
2441         configFile["chassis"][0]["devices"][0]["rails"][0].erase(
2442             "sensor_monitoring");
2443         EXPECT_JSON_VALID(configFile);
2444     }
2445     // Invalid: test rail with no id.
2446     {
2447         json configFile = validConfigFile;
2448         configFile["chassis"][0]["devices"][0]["rails"][0].erase("id");
2449         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2450                             "'id' is a required property");
2451     }
2452     // Invalid: test rail with comments wrong type.
2453     {
2454         json configFile = validConfigFile;
2455         configFile["chassis"][0]["devices"][0]["rails"][0]["comments"] = true;
2456         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2457                             "True is not of type 'array'");
2458     }
2459     // Invalid: test rail with id wrong type.
2460     {
2461         json configFile = validConfigFile;
2462         configFile["chassis"][0]["devices"][0]["rails"][0]["id"] = true;
2463         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2464                             "True is not of type 'string'");
2465     }
2466     // Invalid: test rail with configuration wrong type.
2467     {
2468         json configFile = validConfigFile;
2469         configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"] =
2470             true;
2471         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2472                             "True is not of type 'object'");
2473     }
2474     // Invalid: test rail with sensor_monitoring wrong type.
2475     {
2476         json configFile = validConfigFile;
2477         configFile["chassis"][0]["devices"][0]["rails"][0]
2478                   ["sensor_monitoring"] = true;
2479         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2480                             "True is not of type 'object'");
2481     }
2482     // Invalid: test rail with comments empty array.
2483     {
2484         json configFile = validConfigFile;
2485         configFile["chassis"][0]["devices"][0]["rails"][0]["comments"] =
2486             json::array();
2487         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2488                             "[] is too short");
2489     }
2490     // Invalid: test rail with id wrong format.
2491     {
2492         json configFile = validConfigFile;
2493         configFile["chassis"][0]["devices"][0]["rails"][0]["id"] = "id~";
2494         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2495                             "'id~' does not match '^[A-Za-z0-9_]+$'");
2496     }
2497 }
2498 TEST(ValidateRegulatorsConfigTest, Rule)
2499 {
2500     // valid test comments property, id property,
2501     // action property specified.
2502     {
2503         json configFile = validConfigFile;
2504         EXPECT_JSON_VALID(configFile);
2505     }
2506 
2507     // valid test rule with no comments
2508     {
2509         json configFile = validConfigFile;
2510         configFile["rules"][0].erase("comments");
2511         EXPECT_JSON_VALID(configFile);
2512     }
2513 
2514     // invalid test comments property has invalid value type
2515     {
2516         json configFile = validConfigFile;
2517         configFile["rules"][0]["comments"] = {1};
2518         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2519                             "1 is not of type 'string'");
2520     }
2521 
2522     // invalid test rule with no ID
2523     {
2524         json configFile = validConfigFile;
2525         configFile["rules"][0].erase("id");
2526         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2527                             "'id' is a required property");
2528     }
2529 
2530     // invalid test id property has invalid value type (not string)
2531     {
2532         json configFile = validConfigFile;
2533         configFile["rules"][0]["id"] = true;
2534         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2535                             "True is not of type 'string'");
2536     }
2537 
2538     // invalid test id property has invalid value
2539     {
2540         json configFile = validConfigFile;
2541         configFile["rules"][0]["id"] = "foo%";
2542         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2543                             "'foo%' does not match '^[A-Za-z0-9_]+$'");
2544     }
2545 
2546     // invalid test rule with no actions property
2547     {
2548         json configFile = validConfigFile;
2549         configFile["rules"][0].erase("actions");
2550         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2551                             "'actions' is a required property");
2552     }
2553 
2554     // valid test rule with multiple actions
2555     {
2556         json configFile = validConfigFile;
2557         configFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule";
2558         EXPECT_JSON_VALID(configFile);
2559     }
2560 
2561     // invalid test actions property has invalid value type (not an array)
2562     {
2563         json configFile = validConfigFile;
2564         configFile["rules"][0]["actions"] = 1;
2565         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2566                             "1 is not of type 'array'");
2567     }
2568 
2569     // invalid test actions property has invalid value of action
2570     {
2571         json configFile = validConfigFile;
2572         configFile["rules"][0]["actions"][0] = "foo";
2573         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2574                             "'foo' is not of type 'object'");
2575     }
2576 
2577     // invalid test actions property has empty array
2578     {
2579         json configFile = validConfigFile;
2580         configFile["rules"][0]["actions"] = json::array();
2581         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2582                             "[] is too short");
2583     }
2584 }
2585 TEST(ValidateRegulatorsConfigTest, RunRule)
2586 {
2587     json runRuleFile = validConfigFile;
2588     runRuleFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule";
2589     // Valid: test run_rule.
2590     {
2591         json configFile = runRuleFile;
2592         EXPECT_JSON_VALID(configFile);
2593     }
2594     // Invalid: test run_rule wrong type.
2595     {
2596         json configFile = runRuleFile;
2597         configFile["rules"][0]["actions"][1]["run_rule"] = true;
2598         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2599                             "True is not of type 'string'");
2600     }
2601     // Invalid: test run_rule wrong format.
2602     {
2603         json configFile = runRuleFile;
2604         configFile["rules"][0]["actions"][1]["run_rule"] = "set_voltage_rule%";
2605         EXPECT_JSON_INVALID(
2606             configFile, "Validation failed.",
2607             "'set_voltage_rule%' does not match '^[A-Za-z0-9_]+$'");
2608     }
2609 }
2610 TEST(ValidateRegulatorsConfigTest, SensorMonitoring)
2611 {
2612     // Valid: test rails sensor_monitoring with only property rule id.
2613     {
2614         json configFile = validConfigFile;
2615         EXPECT_JSON_VALID(configFile);
2616     }
2617     // Valid: test rails sensor_monitoring with only property actions.
2618     {
2619         json configFile = validConfigFile;
2620         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2621             .erase("rule_id");
2622         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2623                   ["actions"][0]["compare_presence"]["fru"] =
2624                       "system/chassis/motherboard/cpu3";
2625         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2626                   ["actions"][0]["compare_presence"]["value"] = true;
2627         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2628                   ["comments"][0] = "comments";
2629         EXPECT_JSON_VALID(configFile);
2630     }
2631     // Invalid: test rails sensor_monitoring with both property rule_id and
2632     // actions.
2633     {
2634         json configFile = validConfigFile;
2635         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2636                   ["actions"][0]["compare_presence"]["fru"] =
2637                       "system/chassis/motherboard/cpu3";
2638         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2639                   ["actions"][0]["compare_presence"]["value"] = true;
2640         EXPECT_JSON_INVALID(
2641             configFile, "Validation failed.",
2642             "{'actions': [{'compare_presence': {'fru': "
2643             "'system/chassis/motherboard/cpu3', 'value': True}}], 'rule_id': "
2644             "'read_sensors_rule'} is valid under each of {'required': "
2645             "['actions']}, {'required': ['rule_id']}");
2646     }
2647     // Invalid: test rails sensor_monitoring with no rule_id and actions.
2648     {
2649         json configFile = validConfigFile;
2650         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2651             .erase("rule_id");
2652         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2653                             "'rule_id' is a required property");
2654     }
2655     // Invalid: test rails sensor_monitoring with property comments wrong type.
2656     {
2657         json configFile = validConfigFile;
2658         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2659                   ["comments"] = true;
2660         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2661                             "True is not of type 'array'");
2662     }
2663     // Invalid: test rails sensor_monitoring with property rule_id wrong type.
2664     {
2665         json configFile = validConfigFile;
2666         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2667                   ["rule_id"] = true;
2668         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2669                             "True is not of type 'string'");
2670     }
2671     // Invalid: test rails sensor_monitoring with property actions wrong type.
2672     {
2673         json configFile = validConfigFile;
2674         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2675             .erase("rule_id");
2676         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2677                   ["actions"] = true;
2678         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2679                             "True is not of type 'array'");
2680     }
2681     // Invalid: test rails sensor_monitoring with property rule_id wrong format.
2682     {
2683         json configFile = validConfigFile;
2684         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2685                   ["rule_id"] = "id@";
2686         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2687                             "'id@' does not match '^[A-Za-z0-9_]+$'");
2688     }
2689     // Invalid: test rails sensor_monitoring with property comments empty array.
2690     {
2691         json configFile = validConfigFile;
2692         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2693                   ["comments"] = json::array();
2694         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2695                             "[] is too short");
2696     }
2697     // Invalid: test rails sensor_monitoring with property actions empty array.
2698     {
2699         json configFile = validConfigFile;
2700         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2701             .erase("rule_id");
2702         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2703                   ["actions"] = json::array();
2704         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2705                             "[] is too short");
2706     }
2707 }
2708 TEST(ValidateRegulatorsConfigTest, SetDevice)
2709 {
2710     json setDeviceFile = validConfigFile;
2711     setDeviceFile["rules"][0]["actions"][1]["set_device"] = "vdd_regulator";
2712     // Valid: test set_device.
2713     {
2714         json configFile = setDeviceFile;
2715         EXPECT_JSON_VALID(configFile);
2716     }
2717     // Invalid: test set_device wrong type.
2718     {
2719         json configFile = setDeviceFile;
2720         configFile["rules"][0]["actions"][1]["set_device"] = true;
2721         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2722                             "True is not of type 'string'");
2723     }
2724     // Invalid: test set_device wrong format.
2725     {
2726         json configFile = setDeviceFile;
2727         configFile["rules"][0]["actions"][1]["set_device"] = "io_expander2%";
2728         EXPECT_JSON_INVALID(configFile, "Validation failed.",
2729                             "'io_expander2%' does not match '^[A-Za-z0-9_]+$'");
2730     }
2731 }
2732 TEST(ValidateRegulatorsConfigTest, DuplicateRuleID)
2733 {
2734     // Invalid: test duplicate ID in rule.
2735     {
2736         json configFile = validConfigFile;
2737         configFile["rules"][2]["id"] = "set_voltage_rule";
2738         configFile["rules"][2]["actions"][0]["pmbus_write_vout_command"]
2739                   ["format"] = "linear";
2740         EXPECT_JSON_INVALID(configFile, "Error: Duplicate rule ID.", "");
2741     }
2742 }
2743 TEST(ValidateRegulatorsConfigTest, DuplicateChassisNumber)
2744 {
2745     // Invalid: test duplicate number in chassis.
2746     {
2747         json configFile = validConfigFile;
2748         configFile["chassis"][1]["number"] = 1;
2749         EXPECT_JSON_INVALID(configFile, "Error: Duplicate chassis number.", "");
2750     }
2751 }
2752 TEST(ValidateRegulatorsConfigTest, DuplicateDeviceID)
2753 {
2754     // Invalid: test duplicate ID in device.
2755     {
2756         json configFile = validConfigFile;
2757         configFile["chassis"][0]["devices"][1]["id"] = "vdd_regulator";
2758         configFile["chassis"][0]["devices"][1]["is_regulator"] = true;
2759         configFile["chassis"][0]["devices"][1]["fru"] =
2760             "system/chassis/motherboard/regulator1";
2761         configFile["chassis"][0]["devices"][1]["i2c_interface"]["bus"] = 2;
2762         configFile["chassis"][0]["devices"][1]["i2c_interface"]["address"] =
2763             "0x71";
2764         EXPECT_JSON_INVALID(configFile, "Error: Duplicate device ID.", "");
2765     }
2766 }
2767 TEST(ValidateRegulatorsConfigTest, DuplicateRailID)
2768 {
2769     // Invalid: test duplicate ID in rail.
2770     {
2771         json configFile = validConfigFile;
2772         configFile["chassis"][0]["devices"][0]["rails"][1]["id"] = "vdd";
2773         EXPECT_JSON_INVALID(configFile, "Error: Duplicate rail ID.", "");
2774     }
2775 }
2776 TEST(ValidateRegulatorsConfigTest, DuplicateObjectID)
2777 {
2778     // Invalid: test duplicate object ID in device and rail.
2779     {
2780         json configFile = validConfigFile;
2781         configFile["chassis"][0]["devices"][0]["rails"][1]["id"] =
2782             "vdd_regulator";
2783         EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", "");
2784     }
2785     // Invalid: test duplicate object ID in device and rule.
2786     {
2787         json configFile = validConfigFile;
2788         configFile["rules"][2]["id"] = "vdd_regulator";
2789         configFile["rules"][2]["actions"][0]["pmbus_write_vout_command"]
2790                   ["format"] = "linear";
2791         EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", "");
2792     }
2793     // Invalid: test duplicate object ID in rule and rail.
2794     {
2795         json configFile = validConfigFile;
2796         configFile["chassis"][0]["devices"][0]["rails"][1]["id"] =
2797             "set_voltage_rule";
2798         EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", "");
2799     }
2800 }
2801 TEST(ValidateRegulatorsConfigTest, InfiniteLoops)
2802 {
2803     // Invalid: test run_rule with infinite loop (rules run each other).
2804     {
2805         json configFile = validConfigFile;
2806         configFile["rules"][2]["actions"][0]["run_rule"] = "set_voltage_rule2";
2807         configFile["rules"][2]["id"] = "set_voltage_rule1";
2808         configFile["rules"][3]["actions"][0]["run_rule"] = "set_voltage_rule1";
2809         configFile["rules"][3]["id"] = "set_voltage_rule2";
2810         EXPECT_JSON_INVALID(configFile,
2811                             "Infinite loop caused by run_rule actions.", "");
2812     }
2813     // Invalid: test run_rule with infinite loop (rule runs itself).
2814     {
2815         json configFile = validConfigFile;
2816         configFile["rules"][2]["actions"][0]["run_rule"] = "set_voltage_rule1";
2817         configFile["rules"][2]["id"] = "set_voltage_rule1";
2818         EXPECT_JSON_INVALID(configFile,
2819                             "Infinite loop caused by run_rule actions.", "");
2820     }
2821     // Invalid: test run_rule with infinite loop (indirect loop).
2822     {
2823         json configFile = validConfigFile;
2824         configFile["rules"][2]["actions"][0]["run_rule"] = "set_voltage_rule2";
2825         configFile["rules"][2]["id"] = "set_voltage_rule1";
2826         configFile["rules"][3]["actions"][0]["run_rule"] = "set_voltage_rule3";
2827         configFile["rules"][3]["id"] = "set_voltage_rule2";
2828         configFile["rules"][4]["actions"][0]["run_rule"] = "set_voltage_rule1";
2829         configFile["rules"][4]["id"] = "set_voltage_rule3";
2830         EXPECT_JSON_INVALID(configFile,
2831                             "Infinite loop caused by run_rule actions.", "");
2832     }
2833 }
2834 TEST(ValidateRegulatorsConfigTest, RunRuleValueExists)
2835 {
2836     // Invalid: test run_rule actions specify a rule ID that does not exist.
2837     {
2838         json configFile = validConfigFile;
2839         configFile["rules"][2]["actions"][0]["run_rule"] = "set_voltage_rule2";
2840         configFile["rules"][2]["id"] = "set_voltage_rule1";
2841         EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", "");
2842     }
2843 }
2844 TEST(ValidateRegulatorsConfigTest, SetDeviceValueExists)
2845 {
2846     // Invalid: test set_device actions specify a device ID that does not exist.
2847     {
2848         json configFile = validConfigFile;
2849         configFile["rules"][2]["actions"][0]["set_device"] = "vdd_regulator2";
2850         configFile["rules"][2]["id"] = "set_voltage_rule1";
2851         EXPECT_JSON_INVALID(configFile, "Error: Device ID does not exist.", "");
2852     }
2853 }
2854 TEST(ValidateRegulatorsConfigTest, RuleIDExists)
2855 {
2856     // Invalid: test rule_id property in configuration specifies a rule ID that
2857     // does not exist.
2858     {
2859         json configFile = validConfigFile;
2860         configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] =
2861             "set_voltage_rule2";
2862         EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", "");
2863     }
2864     // Invalid: test rule_id property in presence_detection specifies a rule ID
2865     // that does not exist.
2866     {
2867         json configFile = validConfigFile;
2868         configFile["chassis"][0]["devices"][0]["presence_detection"]
2869                   ["rule_id"] = "set_voltage_rule2";
2870         EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", "");
2871     }
2872     // Invalid: test rule_id property in sensor_monitoring specifies a rule ID
2873     // that does not exist.
2874     {
2875         json configFile = validConfigFile;
2876         configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"]
2877                   ["rule_id"] = "set_voltage_rule2";
2878         EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", "");
2879     }
2880 }
2881 TEST(ValidateRegulatorsConfigTest, NumberOfElementsInMasks)
2882 {
2883     // Invalid: test number of elements in masks not equal to number in values
2884     // in i2c_compare_bytes.
2885     {
2886         json configFile = validConfigFile;
2887         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] =
2888             "0x82";
2889         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = {
2890             "0x02", "0x73"};
2891         configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = {
2892             "0x7F"};
2893         EXPECT_JSON_INVALID(configFile,
2894                             "Error: Invalid i2c_compare_bytes action.", "");
2895     }
2896     // Invalid: test number of elements in masks not equal to number in values
2897     // in i2c_write_bytes.
2898     {
2899         json configFile = validConfigFile;
2900         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] =
2901             "0x82";
2902         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = {
2903             "0x02", "0x73"};
2904         configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = {
2905             "0x7F"};
2906         EXPECT_JSON_INVALID(configFile,
2907                             "Error: Invalid i2c_write_bytes action.", "");
2908     }
2909 }
2910 TEST(ValidateRegulatorsConfigTest, CommandLineSyntax)
2911 {
2912     std::string validateTool =
2913         " ../phosphor-regulators/tools/validate-regulators-config.py ";
2914     std::string schema = " -s ";
2915     std::string schemaFile =
2916         " ../phosphor-regulators/schema/config_schema.json ";
2917     std::string configuration = " -c ";
2918     std::string command;
2919     std::string errorMessage;
2920     std::string outputMessage;
2921     std::string outputMessageHelp =
2922         "usage: validate-regulators-config.py [-h] [-s SCHEMA_FILE]";
2923     int valid = 0;
2924 
2925     TemporaryFile tmpFile;
2926     std::string fileName = tmpFile.getPath().string();
2927     writeDataToFile(validConfigFile, fileName);
2928     // Valid: -s specified
2929     {
2930         command = validateTool + "-s " + schemaFile + configuration + fileName;
2931         expectCommandLineSyntax(errorMessage, outputMessage, command, valid);
2932     }
2933     // Valid: --schema-file specified
2934     {
2935         command = validateTool + "--schema-file " + schemaFile + configuration +
2936                   fileName;
2937         expectCommandLineSyntax(errorMessage, outputMessage, command, valid);
2938     }
2939     // Valid: -c specified
2940     {
2941         command = validateTool + schema + schemaFile + "-c " + fileName;
2942         expectCommandLineSyntax(errorMessage, outputMessage, command, valid);
2943     }
2944     // Valid: --configuration-file specified
2945     {
2946         command = validateTool + schema + schemaFile + "--configuration-file " +
2947                   fileName;
2948         expectCommandLineSyntax(errorMessage, outputMessage, command, valid);
2949     }
2950     // Valid: -h specified
2951     {
2952         command = validateTool + "-h ";
2953         expectCommandLineSyntax(errorMessage, outputMessageHelp, command,
2954                                 valid);
2955     }
2956     // Valid: --help specified
2957     {
2958         command = validateTool + "--help ";
2959         expectCommandLineSyntax(errorMessage, outputMessageHelp, command,
2960                                 valid);
2961     }
2962     // Invalid: -c/--configuration-file not specified
2963     {
2964         command = validateTool + schema + schemaFile;
2965         expectCommandLineSyntax("Error: Configuration file is required.",
2966                                 outputMessageHelp, command, 1);
2967     }
2968     // Invalid: -s/--schema-file not specified
2969     {
2970         command = validateTool + configuration + fileName;
2971         expectCommandLineSyntax("Error: Schema file is required.",
2972                                 outputMessageHelp, command, 1);
2973     }
2974     // Invalid: -c specified more than once
2975     {
2976         command = validateTool + schema + schemaFile + "-c -c " + fileName;
2977         expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2);
2978     }
2979     // Invalid: -s specified more than once
2980     {
2981         command =
2982             validateTool + "-s -s " + schemaFile + configuration + fileName;
2983         expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2);
2984     }
2985     // Invalid: No file name specified after -c
2986     {
2987         command = validateTool + schema + schemaFile + configuration;
2988         expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2);
2989     }
2990     // Invalid: No file name specified after -s
2991     {
2992         command = validateTool + schema + configuration + fileName;
2993         expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2);
2994     }
2995     // Invalid: File specified after -c does not exist
2996     {
2997         command = validateTool + schema + schemaFile + configuration +
2998                   "../notExistFile";
2999         expectCommandLineSyntax("Error: Configuration file does not exist.",
3000                                 outputMessageHelp, command, 1);
3001     }
3002     // Invalid: File specified after -s does not exist
3003     {
3004         command = validateTool + schema + "../notExistFile " + configuration +
3005                   fileName;
3006         expectCommandLineSyntax("Error: Schema file does not exist.",
3007                                 outputMessageHelp, command, 1);
3008     }
3009     // Invalid: File specified after -c is not right data format
3010     {
3011         TemporaryFile wrongFormatFile;
3012         std::string wrongFormatFileName = wrongFormatFile.getPath().string();
3013         std::ofstream out(wrongFormatFileName);
3014         out << "foo";
3015         out.close();
3016         command = validateTool + schema + schemaFile + configuration +
3017                   wrongFormatFileName;
3018         expectCommandLineSyntax(
3019             "Error: Configuration file is not in the JSON format.",
3020             outputMessageHelp, command, 1);
3021     }
3022     // Invalid: File specified after -s is not right data format
3023     {
3024         TemporaryFile wrongFormatFile;
3025         std::string wrongFormatFileName = wrongFormatFile.getPath().string();
3026         std::ofstream out(wrongFormatFileName);
3027         out << "foo";
3028         out.close();
3029         command = validateTool + schema + wrongFormatFileName + configuration +
3030                   fileName;
3031         expectCommandLineSyntax("Error: Schema file is not in the JSON format.",
3032                                 outputMessageHelp, command, 1);
3033     }
3034     // Invalid: File specified after -c is not readable
3035     {
3036         TemporaryFile notReadableFile;
3037         std::string notReadableFileName = notReadableFile.getPath().string();
3038         writeDataToFile(validConfigFile, notReadableFileName);
3039         command = validateTool + schema + schemaFile + configuration +
3040                   notReadableFileName;
3041         chmod(notReadableFileName.c_str(), 0222);
3042         expectCommandLineSyntax("Error: Configuration file is not readable.",
3043                                 outputMessageHelp, command, 1);
3044     }
3045     // Invalid: File specified after -s is not readable
3046     {
3047         TemporaryFile notReadableFile;
3048         std::string notReadableFileName = notReadableFile.getPath().string();
3049         writeDataToFile(validConfigFile, notReadableFileName);
3050         command = validateTool + schema + notReadableFileName + configuration +
3051                   fileName;
3052         chmod(notReadableFileName.c_str(), 0222);
3053         expectCommandLineSyntax("Error: Schema file is not readable.",
3054                                 outputMessageHelp, command, 1);
3055     }
3056     // Invalid: Unexpected parameter specified (like -g)
3057     {
3058         command = validateTool + schema + schemaFile + configuration +
3059                   fileName + " -g";
3060         expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2);
3061     }
3062 }
3063