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