1*0e8c68abSShawn McCarney /**
2*0e8c68abSShawn McCarney  * Copyright © 2020 IBM Corporation
3*0e8c68abSShawn McCarney  *
4*0e8c68abSShawn McCarney  * Licensed under the Apache License, Version 2.0 (the "License");
5*0e8c68abSShawn McCarney  * you may not use this file except in compliance with the License.
6*0e8c68abSShawn McCarney  * You may obtain a copy of the License at
7*0e8c68abSShawn McCarney  *
8*0e8c68abSShawn McCarney  *     http://www.apache.org/licenses/LICENSE-2.0
9*0e8c68abSShawn McCarney  *
10*0e8c68abSShawn McCarney  * Unless required by applicable law or agreed to in writing, software
11*0e8c68abSShawn McCarney  * distributed under the License is distributed on an "AS IS" BASIS,
12*0e8c68abSShawn McCarney  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*0e8c68abSShawn McCarney  * See the License for the specific language governing permissions and
14*0e8c68abSShawn McCarney  * limitations under the License.
15*0e8c68abSShawn McCarney  */
16*0e8c68abSShawn McCarney #include "action.hpp"
17*0e8c68abSShawn McCarney #include "chassis.hpp"
18*0e8c68abSShawn McCarney #include "config_file_parser.hpp"
19*0e8c68abSShawn McCarney #include "config_file_parser_error.hpp"
20*0e8c68abSShawn McCarney #include "pmbus_utils.hpp"
21*0e8c68abSShawn McCarney #include "pmbus_write_vout_command_action.hpp"
22*0e8c68abSShawn McCarney #include "rule.hpp"
23*0e8c68abSShawn McCarney 
24*0e8c68abSShawn McCarney #include <stdlib.h>
25*0e8c68abSShawn McCarney #include <sys/stat.h>
26*0e8c68abSShawn McCarney #include <unistd.h>
27*0e8c68abSShawn McCarney 
28*0e8c68abSShawn McCarney #include <nlohmann/json.hpp>
29*0e8c68abSShawn McCarney 
30*0e8c68abSShawn McCarney #include <cstdint>
31*0e8c68abSShawn McCarney #include <cstring>
32*0e8c68abSShawn McCarney #include <exception>
33*0e8c68abSShawn McCarney #include <filesystem>
34*0e8c68abSShawn McCarney #include <fstream>
35*0e8c68abSShawn McCarney #include <memory>
36*0e8c68abSShawn McCarney #include <optional>
37*0e8c68abSShawn McCarney #include <stdexcept>
38*0e8c68abSShawn McCarney #include <string>
39*0e8c68abSShawn McCarney #include <tuple>
40*0e8c68abSShawn McCarney #include <vector>
41*0e8c68abSShawn McCarney 
42*0e8c68abSShawn McCarney #include <gtest/gtest.h>
43*0e8c68abSShawn McCarney 
44*0e8c68abSShawn McCarney using namespace phosphor::power::regulators;
45*0e8c68abSShawn McCarney using namespace phosphor::power::regulators::config_file_parser;
46*0e8c68abSShawn McCarney using namespace phosphor::power::regulators::config_file_parser::internal;
47*0e8c68abSShawn McCarney using json = nlohmann::json;
48*0e8c68abSShawn McCarney 
49*0e8c68abSShawn McCarney /**
50*0e8c68abSShawn McCarney  * @class TmpFile
51*0e8c68abSShawn McCarney  *
52*0e8c68abSShawn McCarney  * Temporary file.
53*0e8c68abSShawn McCarney  *
54*0e8c68abSShawn McCarney  * File is deleted automatically by the destructor when the object goes out of
55*0e8c68abSShawn McCarney  * scope.
56*0e8c68abSShawn McCarney  */
57*0e8c68abSShawn McCarney class TmpFile
58*0e8c68abSShawn McCarney {
59*0e8c68abSShawn McCarney   public:
60*0e8c68abSShawn McCarney     TmpFile()
61*0e8c68abSShawn McCarney     {
62*0e8c68abSShawn McCarney         int fd = mkstemp(fileName);
63*0e8c68abSShawn McCarney         if (fd == -1)
64*0e8c68abSShawn McCarney         {
65*0e8c68abSShawn McCarney             throw std::runtime_error{"Unable to create temporary file"};
66*0e8c68abSShawn McCarney         }
67*0e8c68abSShawn McCarney         close(fd);
68*0e8c68abSShawn McCarney     }
69*0e8c68abSShawn McCarney 
70*0e8c68abSShawn McCarney     std::string getName()
71*0e8c68abSShawn McCarney     {
72*0e8c68abSShawn McCarney         return fileName;
73*0e8c68abSShawn McCarney     }
74*0e8c68abSShawn McCarney 
75*0e8c68abSShawn McCarney     ~TmpFile()
76*0e8c68abSShawn McCarney     {
77*0e8c68abSShawn McCarney         unlink(fileName);
78*0e8c68abSShawn McCarney     }
79*0e8c68abSShawn McCarney 
80*0e8c68abSShawn McCarney   private:
81*0e8c68abSShawn McCarney     char fileName[17] = "/tmp/temp-XXXXXX";
82*0e8c68abSShawn McCarney };
83*0e8c68abSShawn McCarney 
84*0e8c68abSShawn McCarney void writeConfigFile(const std::filesystem::path& pathName,
85*0e8c68abSShawn McCarney                      const std::string& contents)
86*0e8c68abSShawn McCarney {
87*0e8c68abSShawn McCarney     std::ofstream file{pathName};
88*0e8c68abSShawn McCarney     file << contents;
89*0e8c68abSShawn McCarney }
90*0e8c68abSShawn McCarney 
91*0e8c68abSShawn McCarney void writeConfigFile(const std::filesystem::path& pathName,
92*0e8c68abSShawn McCarney                      const json& contents)
93*0e8c68abSShawn McCarney {
94*0e8c68abSShawn McCarney     std::ofstream file{pathName};
95*0e8c68abSShawn McCarney     file << contents;
96*0e8c68abSShawn McCarney }
97*0e8c68abSShawn McCarney 
98*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, Parse)
99*0e8c68abSShawn McCarney {
100*0e8c68abSShawn McCarney     // Test where works
101*0e8c68abSShawn McCarney     {
102*0e8c68abSShawn McCarney         const json configFileContents = R"(
103*0e8c68abSShawn McCarney             {
104*0e8c68abSShawn McCarney               "rules": [
105*0e8c68abSShawn McCarney                 {
106*0e8c68abSShawn McCarney                   "id": "set_voltage_rule1",
107*0e8c68abSShawn McCarney                   "actions": [
108*0e8c68abSShawn McCarney                     { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
109*0e8c68abSShawn McCarney                   ]
110*0e8c68abSShawn McCarney                 },
111*0e8c68abSShawn McCarney                 {
112*0e8c68abSShawn McCarney                   "id": "set_voltage_rule2",
113*0e8c68abSShawn McCarney                   "actions": [
114*0e8c68abSShawn McCarney                     { "pmbus_write_vout_command": { "volts": 1.33, "format": "linear" } }
115*0e8c68abSShawn McCarney                   ]
116*0e8c68abSShawn McCarney                 }
117*0e8c68abSShawn McCarney               ],
118*0e8c68abSShawn McCarney               "chassis": [
119*0e8c68abSShawn McCarney                 { "number": 1 },
120*0e8c68abSShawn McCarney                 { "number": 2 },
121*0e8c68abSShawn McCarney                 { "number": 3 }
122*0e8c68abSShawn McCarney               ]
123*0e8c68abSShawn McCarney             }
124*0e8c68abSShawn McCarney         )"_json;
125*0e8c68abSShawn McCarney 
126*0e8c68abSShawn McCarney         TmpFile configFile;
127*0e8c68abSShawn McCarney         std::filesystem::path pathName{configFile.getName()};
128*0e8c68abSShawn McCarney         writeConfigFile(pathName, configFileContents);
129*0e8c68abSShawn McCarney 
130*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Rule>> rules{};
131*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Chassis>> chassis{};
132*0e8c68abSShawn McCarney         std::tie(rules, chassis) = parse(pathName);
133*0e8c68abSShawn McCarney 
134*0e8c68abSShawn McCarney         EXPECT_EQ(rules.size(), 2);
135*0e8c68abSShawn McCarney         EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
136*0e8c68abSShawn McCarney         EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
137*0e8c68abSShawn McCarney 
138*0e8c68abSShawn McCarney         // TODO: Not implemented yet
139*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis.size(), 3);
140*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis[0]->getNumber(), 1);
141*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis[1]->getNumber(), 2);
142*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis[2]->getNumber(), 3);
143*0e8c68abSShawn McCarney     }
144*0e8c68abSShawn McCarney 
145*0e8c68abSShawn McCarney     // Test where fails: File does not exist
146*0e8c68abSShawn McCarney     try
147*0e8c68abSShawn McCarney     {
148*0e8c68abSShawn McCarney         std::filesystem::path pathName{"/tmp/non_existent_file"};
149*0e8c68abSShawn McCarney         parse(pathName);
150*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
151*0e8c68abSShawn McCarney     }
152*0e8c68abSShawn McCarney     catch (const ConfigFileParserError& e)
153*0e8c68abSShawn McCarney     {
154*0e8c68abSShawn McCarney         // Expected exception; what() message will vary
155*0e8c68abSShawn McCarney     }
156*0e8c68abSShawn McCarney 
157*0e8c68abSShawn McCarney     // Test where fails: File is not readable
158*0e8c68abSShawn McCarney     try
159*0e8c68abSShawn McCarney     {
160*0e8c68abSShawn McCarney         const json configFileContents = R"(
161*0e8c68abSShawn McCarney             {
162*0e8c68abSShawn McCarney               "chassis": [ { "number": 1 } ]
163*0e8c68abSShawn McCarney             }
164*0e8c68abSShawn McCarney         )"_json;
165*0e8c68abSShawn McCarney 
166*0e8c68abSShawn McCarney         TmpFile configFile;
167*0e8c68abSShawn McCarney         std::filesystem::path pathName{configFile.getName()};
168*0e8c68abSShawn McCarney         writeConfigFile(pathName, configFileContents);
169*0e8c68abSShawn McCarney 
170*0e8c68abSShawn McCarney         chmod(pathName.c_str(), 0222);
171*0e8c68abSShawn McCarney 
172*0e8c68abSShawn McCarney         parse(pathName);
173*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
174*0e8c68abSShawn McCarney     }
175*0e8c68abSShawn McCarney     catch (const ConfigFileParserError& e)
176*0e8c68abSShawn McCarney     {
177*0e8c68abSShawn McCarney         // Expected exception; what() message will vary
178*0e8c68abSShawn McCarney     }
179*0e8c68abSShawn McCarney 
180*0e8c68abSShawn McCarney     // Test where fails: File is not valid JSON
181*0e8c68abSShawn McCarney     try
182*0e8c68abSShawn McCarney     {
183*0e8c68abSShawn McCarney         const std::string configFileContents = "] foo [";
184*0e8c68abSShawn McCarney 
185*0e8c68abSShawn McCarney         TmpFile configFile;
186*0e8c68abSShawn McCarney         std::filesystem::path pathName{configFile.getName()};
187*0e8c68abSShawn McCarney         writeConfigFile(pathName, configFileContents);
188*0e8c68abSShawn McCarney 
189*0e8c68abSShawn McCarney         parse(pathName);
190*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
191*0e8c68abSShawn McCarney     }
192*0e8c68abSShawn McCarney     catch (const ConfigFileParserError& e)
193*0e8c68abSShawn McCarney     {
194*0e8c68abSShawn McCarney         // Expected exception; what() message will vary
195*0e8c68abSShawn McCarney     }
196*0e8c68abSShawn McCarney 
197*0e8c68abSShawn McCarney     // Test where fails: Error when parsing JSON elements
198*0e8c68abSShawn McCarney     try
199*0e8c68abSShawn McCarney     {
200*0e8c68abSShawn McCarney         const json configFileContents = R"( { "foo": "bar" } )"_json;
201*0e8c68abSShawn McCarney 
202*0e8c68abSShawn McCarney         TmpFile configFile;
203*0e8c68abSShawn McCarney         std::filesystem::path pathName{configFile.getName()};
204*0e8c68abSShawn McCarney         writeConfigFile(pathName, configFileContents);
205*0e8c68abSShawn McCarney 
206*0e8c68abSShawn McCarney         parse(pathName);
207*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
208*0e8c68abSShawn McCarney     }
209*0e8c68abSShawn McCarney     catch (const ConfigFileParserError& e)
210*0e8c68abSShawn McCarney     {
211*0e8c68abSShawn McCarney         // Expected exception; what() message will vary
212*0e8c68abSShawn McCarney     }
213*0e8c68abSShawn McCarney }
214*0e8c68abSShawn McCarney 
215*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, GetRequiredProperty)
216*0e8c68abSShawn McCarney {
217*0e8c68abSShawn McCarney     // Test where property exists
218*0e8c68abSShawn McCarney     {
219*0e8c68abSShawn McCarney         const json element = R"( { "format": "linear" } )"_json;
220*0e8c68abSShawn McCarney         const json& propertyElement = getRequiredProperty(element, "format");
221*0e8c68abSShawn McCarney         EXPECT_EQ(propertyElement.get<std::string>(), "linear");
222*0e8c68abSShawn McCarney     }
223*0e8c68abSShawn McCarney 
224*0e8c68abSShawn McCarney     // Test where property does not exist
225*0e8c68abSShawn McCarney     try
226*0e8c68abSShawn McCarney     {
227*0e8c68abSShawn McCarney         const json element = R"( { "volts": 1.03 } )"_json;
228*0e8c68abSShawn McCarney         getRequiredProperty(element, "format");
229*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
230*0e8c68abSShawn McCarney     }
231*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
232*0e8c68abSShawn McCarney     {
233*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: format");
234*0e8c68abSShawn McCarney     }
235*0e8c68abSShawn McCarney }
236*0e8c68abSShawn McCarney 
237*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseAction)
238*0e8c68abSShawn McCarney {
239*0e8c68abSShawn McCarney     // Test where works: comments property specified
240*0e8c68abSShawn McCarney     {
241*0e8c68abSShawn McCarney         const json element = R"(
242*0e8c68abSShawn McCarney             {
243*0e8c68abSShawn McCarney               "comments": [ "Set output voltage." ],
244*0e8c68abSShawn McCarney               "pmbus_write_vout_command": {
245*0e8c68abSShawn McCarney                 "format": "linear"
246*0e8c68abSShawn McCarney               }
247*0e8c68abSShawn McCarney             }
248*0e8c68abSShawn McCarney         )"_json;
249*0e8c68abSShawn McCarney         std::unique_ptr<Action> action = parseAction(element);
250*0e8c68abSShawn McCarney         EXPECT_NE(action.get(), nullptr);
251*0e8c68abSShawn McCarney     }
252*0e8c68abSShawn McCarney 
253*0e8c68abSShawn McCarney     // Test where works: comments property not specified
254*0e8c68abSShawn McCarney     {
255*0e8c68abSShawn McCarney         const json element = R"(
256*0e8c68abSShawn McCarney             {
257*0e8c68abSShawn McCarney               "pmbus_write_vout_command": {
258*0e8c68abSShawn McCarney                 "format": "linear"
259*0e8c68abSShawn McCarney               }
260*0e8c68abSShawn McCarney             }
261*0e8c68abSShawn McCarney         )"_json;
262*0e8c68abSShawn McCarney         std::unique_ptr<Action> action = parseAction(element);
263*0e8c68abSShawn McCarney         EXPECT_NE(action.get(), nullptr);
264*0e8c68abSShawn McCarney     }
265*0e8c68abSShawn McCarney 
266*0e8c68abSShawn McCarney     // Test where works: and action type specified
267*0e8c68abSShawn McCarney     // TODO: Not implemented yet
268*0e8c68abSShawn McCarney 
269*0e8c68abSShawn McCarney     // Test where works: compare_presence action type specified
270*0e8c68abSShawn McCarney     // TODO: Not implemented yet
271*0e8c68abSShawn McCarney 
272*0e8c68abSShawn McCarney     // Test where works: compare_vpd action type specified
273*0e8c68abSShawn McCarney     // TODO: Not implemented yet
274*0e8c68abSShawn McCarney 
275*0e8c68abSShawn McCarney     // Test where works: i2c_compare_bit action type specified
276*0e8c68abSShawn McCarney     // TODO: Not implemented yet
277*0e8c68abSShawn McCarney 
278*0e8c68abSShawn McCarney     // Test where works: i2c_compare_byte action type specified
279*0e8c68abSShawn McCarney     // TODO: Not implemented yet
280*0e8c68abSShawn McCarney 
281*0e8c68abSShawn McCarney     // Test where works: i2c_compare_bytes action type specified
282*0e8c68abSShawn McCarney     // TODO: Not implemented yet
283*0e8c68abSShawn McCarney 
284*0e8c68abSShawn McCarney     // Test where works: i2c_write_bit action type specified
285*0e8c68abSShawn McCarney     // TODO: Not implemented yet
286*0e8c68abSShawn McCarney 
287*0e8c68abSShawn McCarney     // Test where works: i2c_write_byte action type specified
288*0e8c68abSShawn McCarney     // TODO: Not implemented yet
289*0e8c68abSShawn McCarney 
290*0e8c68abSShawn McCarney     // Test where works: i2c_write_bytes action type specified
291*0e8c68abSShawn McCarney     // TODO: Not implemented yet
292*0e8c68abSShawn McCarney 
293*0e8c68abSShawn McCarney     // Test where works: if action type specified
294*0e8c68abSShawn McCarney     // TODO: Not implemented yet
295*0e8c68abSShawn McCarney 
296*0e8c68abSShawn McCarney     // Test where works: not action type specified
297*0e8c68abSShawn McCarney     // TODO: Not implemented yet
298*0e8c68abSShawn McCarney 
299*0e8c68abSShawn McCarney     // Test where works: or action type specified
300*0e8c68abSShawn McCarney     // TODO: Not implemented yet
301*0e8c68abSShawn McCarney 
302*0e8c68abSShawn McCarney     // Test where works: pmbus_read_sensor action type specified
303*0e8c68abSShawn McCarney     // TODO: Not implemented yet
304*0e8c68abSShawn McCarney 
305*0e8c68abSShawn McCarney     // Test where works: pmbus_write_vout_command action type specified
306*0e8c68abSShawn McCarney     {
307*0e8c68abSShawn McCarney         const json element = R"(
308*0e8c68abSShawn McCarney             {
309*0e8c68abSShawn McCarney               "pmbus_write_vout_command": {
310*0e8c68abSShawn McCarney                 "format": "linear"
311*0e8c68abSShawn McCarney               }
312*0e8c68abSShawn McCarney             }
313*0e8c68abSShawn McCarney         )"_json;
314*0e8c68abSShawn McCarney         std::unique_ptr<Action> action = parseAction(element);
315*0e8c68abSShawn McCarney         EXPECT_NE(action.get(), nullptr);
316*0e8c68abSShawn McCarney     }
317*0e8c68abSShawn McCarney 
318*0e8c68abSShawn McCarney     // Test where works: run_rule action type specified
319*0e8c68abSShawn McCarney     // TODO: Not implemented yet
320*0e8c68abSShawn McCarney 
321*0e8c68abSShawn McCarney     // Test where works: set_device action type specified
322*0e8c68abSShawn McCarney     // TODO: Not implemented yet
323*0e8c68abSShawn McCarney 
324*0e8c68abSShawn McCarney     // Test where fails: Element is not an object
325*0e8c68abSShawn McCarney     try
326*0e8c68abSShawn McCarney     {
327*0e8c68abSShawn McCarney         const json element = R"( [ "0xFF", "0x01" ] )"_json;
328*0e8c68abSShawn McCarney         parseAction(element);
329*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
330*0e8c68abSShawn McCarney     }
331*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
332*0e8c68abSShawn McCarney     {
333*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
334*0e8c68abSShawn McCarney     }
335*0e8c68abSShawn McCarney 
336*0e8c68abSShawn McCarney     // Test where fails: No action type specified
337*0e8c68abSShawn McCarney     try
338*0e8c68abSShawn McCarney     {
339*0e8c68abSShawn McCarney         const json element = R"(
340*0e8c68abSShawn McCarney             {
341*0e8c68abSShawn McCarney               "comments": [ "Set output voltage." ]
342*0e8c68abSShawn McCarney             }
343*0e8c68abSShawn McCarney         )"_json;
344*0e8c68abSShawn McCarney         parseAction(element);
345*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
346*0e8c68abSShawn McCarney     }
347*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
348*0e8c68abSShawn McCarney     {
349*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required action type property missing");
350*0e8c68abSShawn McCarney     }
351*0e8c68abSShawn McCarney 
352*0e8c68abSShawn McCarney     // Test where fails: Multiple action types specified
353*0e8c68abSShawn McCarney     // TODO: Implement after another action type is supported
354*0e8c68abSShawn McCarney 
355*0e8c68abSShawn McCarney     // Test where fails: Invalid property specified
356*0e8c68abSShawn McCarney     try
357*0e8c68abSShawn McCarney     {
358*0e8c68abSShawn McCarney         const json element = R"(
359*0e8c68abSShawn McCarney             {
360*0e8c68abSShawn McCarney               "remarks": [ "Set output voltage." ],
361*0e8c68abSShawn McCarney               "pmbus_write_vout_command": {
362*0e8c68abSShawn McCarney                 "format": "linear"
363*0e8c68abSShawn McCarney               }
364*0e8c68abSShawn McCarney             }
365*0e8c68abSShawn McCarney         )"_json;
366*0e8c68abSShawn McCarney         parseAction(element);
367*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
368*0e8c68abSShawn McCarney     }
369*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
370*0e8c68abSShawn McCarney     {
371*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
372*0e8c68abSShawn McCarney     }
373*0e8c68abSShawn McCarney }
374*0e8c68abSShawn McCarney 
375*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseActionArray)
376*0e8c68abSShawn McCarney {
377*0e8c68abSShawn McCarney     // Test where works
378*0e8c68abSShawn McCarney     {
379*0e8c68abSShawn McCarney         const json element = R"(
380*0e8c68abSShawn McCarney             [
381*0e8c68abSShawn McCarney               { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
382*0e8c68abSShawn McCarney               { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
383*0e8c68abSShawn McCarney             ]
384*0e8c68abSShawn McCarney         )"_json;
385*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Action>> actions =
386*0e8c68abSShawn McCarney             parseActionArray(element);
387*0e8c68abSShawn McCarney         EXPECT_EQ(actions.size(), 2);
388*0e8c68abSShawn McCarney     }
389*0e8c68abSShawn McCarney 
390*0e8c68abSShawn McCarney     // Test where fails: Element is not an array
391*0e8c68abSShawn McCarney     try
392*0e8c68abSShawn McCarney     {
393*0e8c68abSShawn McCarney         const json element = R"(
394*0e8c68abSShawn McCarney             {
395*0e8c68abSShawn McCarney               "foo": "bar"
396*0e8c68abSShawn McCarney             }
397*0e8c68abSShawn McCarney         )"_json;
398*0e8c68abSShawn McCarney         parseActionArray(element);
399*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
400*0e8c68abSShawn McCarney     }
401*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
402*0e8c68abSShawn McCarney     {
403*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
404*0e8c68abSShawn McCarney     }
405*0e8c68abSShawn McCarney }
406*0e8c68abSShawn McCarney 
407*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseBoolean)
408*0e8c68abSShawn McCarney {
409*0e8c68abSShawn McCarney     // Test where works: true
410*0e8c68abSShawn McCarney     {
411*0e8c68abSShawn McCarney         const json element = R"( true )"_json;
412*0e8c68abSShawn McCarney         bool value = parseBoolean(element);
413*0e8c68abSShawn McCarney         EXPECT_EQ(value, true);
414*0e8c68abSShawn McCarney     }
415*0e8c68abSShawn McCarney 
416*0e8c68abSShawn McCarney     // Test where works: false
417*0e8c68abSShawn McCarney     {
418*0e8c68abSShawn McCarney         const json element = R"( false )"_json;
419*0e8c68abSShawn McCarney         bool value = parseBoolean(element);
420*0e8c68abSShawn McCarney         EXPECT_EQ(value, false);
421*0e8c68abSShawn McCarney     }
422*0e8c68abSShawn McCarney 
423*0e8c68abSShawn McCarney     // Test where fails: Element is not a boolean
424*0e8c68abSShawn McCarney     try
425*0e8c68abSShawn McCarney     {
426*0e8c68abSShawn McCarney         const json element = R"( 1 )"_json;
427*0e8c68abSShawn McCarney         parseBoolean(element);
428*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
429*0e8c68abSShawn McCarney     }
430*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
431*0e8c68abSShawn McCarney     {
432*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
433*0e8c68abSShawn McCarney     }
434*0e8c68abSShawn McCarney }
435*0e8c68abSShawn McCarney 
436*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseChassisArray)
437*0e8c68abSShawn McCarney {
438*0e8c68abSShawn McCarney     // TODO: Not implemented yet
439*0e8c68abSShawn McCarney }
440*0e8c68abSShawn McCarney 
441*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseDouble)
442*0e8c68abSShawn McCarney {
443*0e8c68abSShawn McCarney     // Test where works: floating point value
444*0e8c68abSShawn McCarney     {
445*0e8c68abSShawn McCarney         const json element = R"( 1.03 )"_json;
446*0e8c68abSShawn McCarney         double value = parseDouble(element);
447*0e8c68abSShawn McCarney         EXPECT_EQ(value, 1.03);
448*0e8c68abSShawn McCarney     }
449*0e8c68abSShawn McCarney 
450*0e8c68abSShawn McCarney     // Test where works: integer value
451*0e8c68abSShawn McCarney     {
452*0e8c68abSShawn McCarney         const json element = R"( 24 )"_json;
453*0e8c68abSShawn McCarney         double value = parseDouble(element);
454*0e8c68abSShawn McCarney         EXPECT_EQ(value, 24.0);
455*0e8c68abSShawn McCarney     }
456*0e8c68abSShawn McCarney 
457*0e8c68abSShawn McCarney     // Test where fails: Element is not a number
458*0e8c68abSShawn McCarney     try
459*0e8c68abSShawn McCarney     {
460*0e8c68abSShawn McCarney         const json element = R"( true )"_json;
461*0e8c68abSShawn McCarney         parseDouble(element);
462*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
463*0e8c68abSShawn McCarney     }
464*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
465*0e8c68abSShawn McCarney     {
466*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a number");
467*0e8c68abSShawn McCarney     }
468*0e8c68abSShawn McCarney }
469*0e8c68abSShawn McCarney 
470*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseInt8)
471*0e8c68abSShawn McCarney {
472*0e8c68abSShawn McCarney     // Test where works: INT8_MIN
473*0e8c68abSShawn McCarney     {
474*0e8c68abSShawn McCarney         const json element = R"( -128 )"_json;
475*0e8c68abSShawn McCarney         int8_t value = parseInt8(element);
476*0e8c68abSShawn McCarney         EXPECT_EQ(value, -128);
477*0e8c68abSShawn McCarney     }
478*0e8c68abSShawn McCarney 
479*0e8c68abSShawn McCarney     // Test where works: INT8_MAX
480*0e8c68abSShawn McCarney     {
481*0e8c68abSShawn McCarney         const json element = R"( 127 )"_json;
482*0e8c68abSShawn McCarney         int8_t value = parseInt8(element);
483*0e8c68abSShawn McCarney         EXPECT_EQ(value, 127);
484*0e8c68abSShawn McCarney     }
485*0e8c68abSShawn McCarney 
486*0e8c68abSShawn McCarney     // Test where fails: Element is not an integer
487*0e8c68abSShawn McCarney     try
488*0e8c68abSShawn McCarney     {
489*0e8c68abSShawn McCarney         const json element = R"( 1.03 )"_json;
490*0e8c68abSShawn McCarney         parseInt8(element);
491*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
492*0e8c68abSShawn McCarney     }
493*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
494*0e8c68abSShawn McCarney     {
495*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an integer");
496*0e8c68abSShawn McCarney     }
497*0e8c68abSShawn McCarney 
498*0e8c68abSShawn McCarney     // Test where fails: Value < INT8_MIN
499*0e8c68abSShawn McCarney     try
500*0e8c68abSShawn McCarney     {
501*0e8c68abSShawn McCarney         const json element = R"( -129 )"_json;
502*0e8c68abSShawn McCarney         parseInt8(element);
503*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
504*0e8c68abSShawn McCarney     }
505*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
506*0e8c68abSShawn McCarney     {
507*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
508*0e8c68abSShawn McCarney     }
509*0e8c68abSShawn McCarney 
510*0e8c68abSShawn McCarney     // Test where fails: Value > INT8_MAX
511*0e8c68abSShawn McCarney     try
512*0e8c68abSShawn McCarney     {
513*0e8c68abSShawn McCarney         const json element = R"( 128 )"_json;
514*0e8c68abSShawn McCarney         parseInt8(element);
515*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
516*0e8c68abSShawn McCarney     }
517*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
518*0e8c68abSShawn McCarney     {
519*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
520*0e8c68abSShawn McCarney     }
521*0e8c68abSShawn McCarney }
522*0e8c68abSShawn McCarney 
523*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParsePMBusWriteVoutCommand)
524*0e8c68abSShawn McCarney {
525*0e8c68abSShawn McCarney     // Test where works: Only required properties specified
526*0e8c68abSShawn McCarney     {
527*0e8c68abSShawn McCarney         const json element = R"(
528*0e8c68abSShawn McCarney             {
529*0e8c68abSShawn McCarney               "format": "linear"
530*0e8c68abSShawn McCarney             }
531*0e8c68abSShawn McCarney         )"_json;
532*0e8c68abSShawn McCarney         std::unique_ptr<PMBusWriteVoutCommandAction> action =
533*0e8c68abSShawn McCarney             parsePMBusWriteVoutCommand(element);
534*0e8c68abSShawn McCarney         EXPECT_EQ(action->getVolts().has_value(), false);
535*0e8c68abSShawn McCarney         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
536*0e8c68abSShawn McCarney         EXPECT_EQ(action->getExponent().has_value(), false);
537*0e8c68abSShawn McCarney         EXPECT_EQ(action->isVerified(), false);
538*0e8c68abSShawn McCarney     }
539*0e8c68abSShawn McCarney 
540*0e8c68abSShawn McCarney     // Test where works: All properties specified
541*0e8c68abSShawn McCarney     {
542*0e8c68abSShawn McCarney         const json element = R"(
543*0e8c68abSShawn McCarney             {
544*0e8c68abSShawn McCarney               "volts": 1.03,
545*0e8c68abSShawn McCarney               "format": "linear",
546*0e8c68abSShawn McCarney               "exponent": -8,
547*0e8c68abSShawn McCarney               "is_verified": true
548*0e8c68abSShawn McCarney             }
549*0e8c68abSShawn McCarney         )"_json;
550*0e8c68abSShawn McCarney         std::unique_ptr<PMBusWriteVoutCommandAction> action =
551*0e8c68abSShawn McCarney             parsePMBusWriteVoutCommand(element);
552*0e8c68abSShawn McCarney         EXPECT_EQ(action->getVolts().has_value(), true);
553*0e8c68abSShawn McCarney         EXPECT_EQ(action->getVolts().value(), 1.03);
554*0e8c68abSShawn McCarney         EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
555*0e8c68abSShawn McCarney         EXPECT_EQ(action->getExponent().has_value(), true);
556*0e8c68abSShawn McCarney         EXPECT_EQ(action->getExponent().value(), -8);
557*0e8c68abSShawn McCarney         EXPECT_EQ(action->isVerified(), true);
558*0e8c68abSShawn McCarney     }
559*0e8c68abSShawn McCarney 
560*0e8c68abSShawn McCarney     // Test where fails: Element is not an object
561*0e8c68abSShawn McCarney     try
562*0e8c68abSShawn McCarney     {
563*0e8c68abSShawn McCarney         const json element = R"( [ "0xFF", "0x01" ] )"_json;
564*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
565*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
566*0e8c68abSShawn McCarney     }
567*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
568*0e8c68abSShawn McCarney     {
569*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
570*0e8c68abSShawn McCarney     }
571*0e8c68abSShawn McCarney 
572*0e8c68abSShawn McCarney     // Test where fails: volts value is invalid
573*0e8c68abSShawn McCarney     try
574*0e8c68abSShawn McCarney     {
575*0e8c68abSShawn McCarney         const json element = R"(
576*0e8c68abSShawn McCarney             {
577*0e8c68abSShawn McCarney               "volts": "foo",
578*0e8c68abSShawn McCarney               "format": "linear"
579*0e8c68abSShawn McCarney             }
580*0e8c68abSShawn McCarney         )"_json;
581*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
582*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
583*0e8c68abSShawn McCarney     }
584*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
585*0e8c68abSShawn McCarney     {
586*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a number");
587*0e8c68abSShawn McCarney     }
588*0e8c68abSShawn McCarney 
589*0e8c68abSShawn McCarney     // Test where fails: Required format property not specified
590*0e8c68abSShawn McCarney     try
591*0e8c68abSShawn McCarney     {
592*0e8c68abSShawn McCarney         const json element = R"(
593*0e8c68abSShawn McCarney             {
594*0e8c68abSShawn McCarney               "volts": 1.03,
595*0e8c68abSShawn McCarney               "is_verified": true
596*0e8c68abSShawn McCarney             }
597*0e8c68abSShawn McCarney         )"_json;
598*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
599*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
600*0e8c68abSShawn McCarney     }
601*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
602*0e8c68abSShawn McCarney     {
603*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: format");
604*0e8c68abSShawn McCarney     }
605*0e8c68abSShawn McCarney 
606*0e8c68abSShawn McCarney     // Test where fails: format value is invalid
607*0e8c68abSShawn McCarney     try
608*0e8c68abSShawn McCarney     {
609*0e8c68abSShawn McCarney         const json element = R"(
610*0e8c68abSShawn McCarney             {
611*0e8c68abSShawn McCarney               "format": "linear_11"
612*0e8c68abSShawn McCarney             }
613*0e8c68abSShawn McCarney         )"_json;
614*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
615*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
616*0e8c68abSShawn McCarney     }
617*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
618*0e8c68abSShawn McCarney     {
619*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Invalid format value: linear_11");
620*0e8c68abSShawn McCarney     }
621*0e8c68abSShawn McCarney 
622*0e8c68abSShawn McCarney     // Test where fails: exponent value is invalid
623*0e8c68abSShawn McCarney     try
624*0e8c68abSShawn McCarney     {
625*0e8c68abSShawn McCarney         const json element = R"(
626*0e8c68abSShawn McCarney             {
627*0e8c68abSShawn McCarney               "format": "linear",
628*0e8c68abSShawn McCarney               "exponent": 1.3
629*0e8c68abSShawn McCarney             }
630*0e8c68abSShawn McCarney         )"_json;
631*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
632*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
633*0e8c68abSShawn McCarney     }
634*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
635*0e8c68abSShawn McCarney     {
636*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an integer");
637*0e8c68abSShawn McCarney     }
638*0e8c68abSShawn McCarney 
639*0e8c68abSShawn McCarney     // Test where fails: is_verified value is invalid
640*0e8c68abSShawn McCarney     try
641*0e8c68abSShawn McCarney     {
642*0e8c68abSShawn McCarney         const json element = R"(
643*0e8c68abSShawn McCarney             {
644*0e8c68abSShawn McCarney               "format": "linear",
645*0e8c68abSShawn McCarney               "is_verified": "true"
646*0e8c68abSShawn McCarney             }
647*0e8c68abSShawn McCarney         )"_json;
648*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
649*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
650*0e8c68abSShawn McCarney     }
651*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
652*0e8c68abSShawn McCarney     {
653*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
654*0e8c68abSShawn McCarney     }
655*0e8c68abSShawn McCarney 
656*0e8c68abSShawn McCarney     // Test where fails: Invalid property specified
657*0e8c68abSShawn McCarney     try
658*0e8c68abSShawn McCarney     {
659*0e8c68abSShawn McCarney         const json element = R"(
660*0e8c68abSShawn McCarney             {
661*0e8c68abSShawn McCarney               "format": "linear",
662*0e8c68abSShawn McCarney               "foo": "bar"
663*0e8c68abSShawn McCarney             }
664*0e8c68abSShawn McCarney         )"_json;
665*0e8c68abSShawn McCarney         parsePMBusWriteVoutCommand(element);
666*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
667*0e8c68abSShawn McCarney     }
668*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
669*0e8c68abSShawn McCarney     {
670*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
671*0e8c68abSShawn McCarney     }
672*0e8c68abSShawn McCarney }
673*0e8c68abSShawn McCarney 
674*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseRoot)
675*0e8c68abSShawn McCarney {
676*0e8c68abSShawn McCarney     // Test where works: Only required properties specified
677*0e8c68abSShawn McCarney     {
678*0e8c68abSShawn McCarney         const json element = R"(
679*0e8c68abSShawn McCarney             {
680*0e8c68abSShawn McCarney               "chassis": [
681*0e8c68abSShawn McCarney                 { "number": 1 }
682*0e8c68abSShawn McCarney               ]
683*0e8c68abSShawn McCarney             }
684*0e8c68abSShawn McCarney         )"_json;
685*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Rule>> rules{};
686*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Chassis>> chassis{};
687*0e8c68abSShawn McCarney         std::tie(rules, chassis) = parseRoot(element);
688*0e8c68abSShawn McCarney         EXPECT_EQ(rules.size(), 0);
689*0e8c68abSShawn McCarney         // TODO: Not implemented yet
690*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis.size(), 1);
691*0e8c68abSShawn McCarney     }
692*0e8c68abSShawn McCarney 
693*0e8c68abSShawn McCarney     // Test where works: All properties specified
694*0e8c68abSShawn McCarney     {
695*0e8c68abSShawn McCarney         const json element = R"(
696*0e8c68abSShawn McCarney             {
697*0e8c68abSShawn McCarney               "comments": [ "Config file for a FooBar one-chassis system" ],
698*0e8c68abSShawn McCarney               "rules": [
699*0e8c68abSShawn McCarney                 {
700*0e8c68abSShawn McCarney                   "id": "set_voltage_rule",
701*0e8c68abSShawn McCarney                   "actions": [
702*0e8c68abSShawn McCarney                     { "pmbus_write_vout_command": { "format": "linear" } }
703*0e8c68abSShawn McCarney                   ]
704*0e8c68abSShawn McCarney                 }
705*0e8c68abSShawn McCarney               ],
706*0e8c68abSShawn McCarney               "chassis": [
707*0e8c68abSShawn McCarney                 { "number": 1 },
708*0e8c68abSShawn McCarney                 { "number": 3 }
709*0e8c68abSShawn McCarney               ]
710*0e8c68abSShawn McCarney             }
711*0e8c68abSShawn McCarney         )"_json;
712*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Rule>> rules{};
713*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Chassis>> chassis{};
714*0e8c68abSShawn McCarney         std::tie(rules, chassis) = parseRoot(element);
715*0e8c68abSShawn McCarney         EXPECT_EQ(rules.size(), 1);
716*0e8c68abSShawn McCarney         // TODO: Not implemented yet
717*0e8c68abSShawn McCarney         // EXPECT_EQ(chassis.size(), 2);
718*0e8c68abSShawn McCarney     }
719*0e8c68abSShawn McCarney 
720*0e8c68abSShawn McCarney     // Test where fails: Element is not an object
721*0e8c68abSShawn McCarney     try
722*0e8c68abSShawn McCarney     {
723*0e8c68abSShawn McCarney         const json element = R"( [ "0xFF", "0x01" ] )"_json;
724*0e8c68abSShawn McCarney         parseRoot(element);
725*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
726*0e8c68abSShawn McCarney     }
727*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
728*0e8c68abSShawn McCarney     {
729*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
730*0e8c68abSShawn McCarney     }
731*0e8c68abSShawn McCarney 
732*0e8c68abSShawn McCarney     // Test where fails: chassis property not specified
733*0e8c68abSShawn McCarney     try
734*0e8c68abSShawn McCarney     {
735*0e8c68abSShawn McCarney         const json element = R"(
736*0e8c68abSShawn McCarney             {
737*0e8c68abSShawn McCarney               "rules": [
738*0e8c68abSShawn McCarney                 {
739*0e8c68abSShawn McCarney                   "id": "set_voltage_rule",
740*0e8c68abSShawn McCarney                   "actions": [
741*0e8c68abSShawn McCarney                     { "pmbus_write_vout_command": { "format": "linear" } }
742*0e8c68abSShawn McCarney                   ]
743*0e8c68abSShawn McCarney                 }
744*0e8c68abSShawn McCarney               ]
745*0e8c68abSShawn McCarney             }
746*0e8c68abSShawn McCarney         )"_json;
747*0e8c68abSShawn McCarney         parseRoot(element);
748*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
749*0e8c68abSShawn McCarney     }
750*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
751*0e8c68abSShawn McCarney     {
752*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: chassis");
753*0e8c68abSShawn McCarney     }
754*0e8c68abSShawn McCarney 
755*0e8c68abSShawn McCarney     // Test where fails: Invalid property specified
756*0e8c68abSShawn McCarney     try
757*0e8c68abSShawn McCarney     {
758*0e8c68abSShawn McCarney         const json element = R"(
759*0e8c68abSShawn McCarney             {
760*0e8c68abSShawn McCarney               "remarks": [ "Config file for a FooBar one-chassis system" ],
761*0e8c68abSShawn McCarney               "chassis": [
762*0e8c68abSShawn McCarney                 { "number": 1 }
763*0e8c68abSShawn McCarney               ]
764*0e8c68abSShawn McCarney             }
765*0e8c68abSShawn McCarney         )"_json;
766*0e8c68abSShawn McCarney         parseRoot(element);
767*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
768*0e8c68abSShawn McCarney     }
769*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
770*0e8c68abSShawn McCarney     {
771*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
772*0e8c68abSShawn McCarney     }
773*0e8c68abSShawn McCarney }
774*0e8c68abSShawn McCarney 
775*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseRule)
776*0e8c68abSShawn McCarney {
777*0e8c68abSShawn McCarney     // Test where works: comments property specified
778*0e8c68abSShawn McCarney     {
779*0e8c68abSShawn McCarney         const json element = R"(
780*0e8c68abSShawn McCarney             {
781*0e8c68abSShawn McCarney               "comments": [ "Set voltage rule" ],
782*0e8c68abSShawn McCarney               "id": "set_voltage_rule",
783*0e8c68abSShawn McCarney               "actions": [
784*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
785*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
786*0e8c68abSShawn McCarney               ]
787*0e8c68abSShawn McCarney             }
788*0e8c68abSShawn McCarney         )"_json;
789*0e8c68abSShawn McCarney         std::unique_ptr<Rule> rule = parseRule(element);
790*0e8c68abSShawn McCarney         EXPECT_EQ(rule->getID(), "set_voltage_rule");
791*0e8c68abSShawn McCarney         EXPECT_EQ(rule->getActions().size(), 2);
792*0e8c68abSShawn McCarney     }
793*0e8c68abSShawn McCarney 
794*0e8c68abSShawn McCarney     // Test where works: comments property not specified
795*0e8c68abSShawn McCarney     {
796*0e8c68abSShawn McCarney         const json element = R"(
797*0e8c68abSShawn McCarney             {
798*0e8c68abSShawn McCarney               "id": "set_voltage_rule",
799*0e8c68abSShawn McCarney               "actions": [
800*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
801*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } },
802*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.05, "format": "linear" } }
803*0e8c68abSShawn McCarney               ]
804*0e8c68abSShawn McCarney             }
805*0e8c68abSShawn McCarney         )"_json;
806*0e8c68abSShawn McCarney         std::unique_ptr<Rule> rule = parseRule(element);
807*0e8c68abSShawn McCarney         EXPECT_EQ(rule->getID(), "set_voltage_rule");
808*0e8c68abSShawn McCarney         EXPECT_EQ(rule->getActions().size(), 3);
809*0e8c68abSShawn McCarney     }
810*0e8c68abSShawn McCarney 
811*0e8c68abSShawn McCarney     // Test where fails: Element is not an object
812*0e8c68abSShawn McCarney     try
813*0e8c68abSShawn McCarney     {
814*0e8c68abSShawn McCarney         const json element = R"( [ "0xFF", "0x01" ] )"_json;
815*0e8c68abSShawn McCarney         parseRule(element);
816*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
817*0e8c68abSShawn McCarney     }
818*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
819*0e8c68abSShawn McCarney     {
820*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
821*0e8c68abSShawn McCarney     }
822*0e8c68abSShawn McCarney 
823*0e8c68abSShawn McCarney     // Test where fails: id property not specified
824*0e8c68abSShawn McCarney     try
825*0e8c68abSShawn McCarney     {
826*0e8c68abSShawn McCarney         const json element = R"(
827*0e8c68abSShawn McCarney             {
828*0e8c68abSShawn McCarney               "actions": [
829*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
830*0e8c68abSShawn McCarney               ]
831*0e8c68abSShawn McCarney             }
832*0e8c68abSShawn McCarney         )"_json;
833*0e8c68abSShawn McCarney         parseRule(element);
834*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
835*0e8c68abSShawn McCarney     }
836*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
837*0e8c68abSShawn McCarney     {
838*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: id");
839*0e8c68abSShawn McCarney     }
840*0e8c68abSShawn McCarney 
841*0e8c68abSShawn McCarney     // Test where fails: id property is invalid
842*0e8c68abSShawn McCarney     try
843*0e8c68abSShawn McCarney     {
844*0e8c68abSShawn McCarney         const json element = R"(
845*0e8c68abSShawn McCarney             {
846*0e8c68abSShawn McCarney               "id": "",
847*0e8c68abSShawn McCarney               "actions": [
848*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
849*0e8c68abSShawn McCarney               ]
850*0e8c68abSShawn McCarney             }
851*0e8c68abSShawn McCarney         )"_json;
852*0e8c68abSShawn McCarney         parseRule(element);
853*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
854*0e8c68abSShawn McCarney     }
855*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
856*0e8c68abSShawn McCarney     {
857*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an empty string");
858*0e8c68abSShawn McCarney     }
859*0e8c68abSShawn McCarney 
860*0e8c68abSShawn McCarney     // Test where fails: actions property not specified
861*0e8c68abSShawn McCarney     try
862*0e8c68abSShawn McCarney     {
863*0e8c68abSShawn McCarney         const json element = R"(
864*0e8c68abSShawn McCarney             {
865*0e8c68abSShawn McCarney               "comments": [ "Set voltage rule" ],
866*0e8c68abSShawn McCarney               "id": "set_voltage_rule"
867*0e8c68abSShawn McCarney             }
868*0e8c68abSShawn McCarney         )"_json;
869*0e8c68abSShawn McCarney         parseRule(element);
870*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
871*0e8c68abSShawn McCarney     }
872*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
873*0e8c68abSShawn McCarney     {
874*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: actions");
875*0e8c68abSShawn McCarney     }
876*0e8c68abSShawn McCarney 
877*0e8c68abSShawn McCarney     // Test where fails: actions property is invalid
878*0e8c68abSShawn McCarney     try
879*0e8c68abSShawn McCarney     {
880*0e8c68abSShawn McCarney         const json element = R"(
881*0e8c68abSShawn McCarney             {
882*0e8c68abSShawn McCarney               "id": "set_voltage_rule",
883*0e8c68abSShawn McCarney               "actions": true
884*0e8c68abSShawn McCarney             }
885*0e8c68abSShawn McCarney         )"_json;
886*0e8c68abSShawn McCarney         parseRule(element);
887*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
888*0e8c68abSShawn McCarney     }
889*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
890*0e8c68abSShawn McCarney     {
891*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
892*0e8c68abSShawn McCarney     }
893*0e8c68abSShawn McCarney 
894*0e8c68abSShawn McCarney     // Test where fails: Invalid property specified
895*0e8c68abSShawn McCarney     try
896*0e8c68abSShawn McCarney     {
897*0e8c68abSShawn McCarney         const json element = R"(
898*0e8c68abSShawn McCarney             {
899*0e8c68abSShawn McCarney               "remarks": [ "Set voltage rule" ],
900*0e8c68abSShawn McCarney               "id": "set_voltage_rule",
901*0e8c68abSShawn McCarney               "actions": [
902*0e8c68abSShawn McCarney                 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
903*0e8c68abSShawn McCarney               ]
904*0e8c68abSShawn McCarney             }
905*0e8c68abSShawn McCarney         )"_json;
906*0e8c68abSShawn McCarney         parseRule(element);
907*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
908*0e8c68abSShawn McCarney     }
909*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
910*0e8c68abSShawn McCarney     {
911*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
912*0e8c68abSShawn McCarney     }
913*0e8c68abSShawn McCarney }
914*0e8c68abSShawn McCarney 
915*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseRuleArray)
916*0e8c68abSShawn McCarney {
917*0e8c68abSShawn McCarney     // Test where works
918*0e8c68abSShawn McCarney     {
919*0e8c68abSShawn McCarney         const json element = R"(
920*0e8c68abSShawn McCarney             [
921*0e8c68abSShawn McCarney               {
922*0e8c68abSShawn McCarney                 "id": "set_voltage_rule1",
923*0e8c68abSShawn McCarney                 "actions": [
924*0e8c68abSShawn McCarney                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
925*0e8c68abSShawn McCarney                 ]
926*0e8c68abSShawn McCarney               },
927*0e8c68abSShawn McCarney               {
928*0e8c68abSShawn McCarney                 "id": "set_voltage_rule2",
929*0e8c68abSShawn McCarney                 "actions": [
930*0e8c68abSShawn McCarney                   { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
931*0e8c68abSShawn McCarney                   { "pmbus_write_vout_command": { "volts": 1.11, "format": "linear" } }
932*0e8c68abSShawn McCarney                 ]
933*0e8c68abSShawn McCarney               }
934*0e8c68abSShawn McCarney             ]
935*0e8c68abSShawn McCarney         )"_json;
936*0e8c68abSShawn McCarney         std::vector<std::unique_ptr<Rule>> rules = parseRuleArray(element);
937*0e8c68abSShawn McCarney         EXPECT_EQ(rules.size(), 2);
938*0e8c68abSShawn McCarney         EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
939*0e8c68abSShawn McCarney         EXPECT_EQ(rules[0]->getActions().size(), 1);
940*0e8c68abSShawn McCarney         EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
941*0e8c68abSShawn McCarney         EXPECT_EQ(rules[1]->getActions().size(), 2);
942*0e8c68abSShawn McCarney     }
943*0e8c68abSShawn McCarney 
944*0e8c68abSShawn McCarney     // Test where fails: Element is not an array
945*0e8c68abSShawn McCarney     try
946*0e8c68abSShawn McCarney     {
947*0e8c68abSShawn McCarney         const json element = R"( { "id": "set_voltage_rule" } )"_json;
948*0e8c68abSShawn McCarney         parseRuleArray(element);
949*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
950*0e8c68abSShawn McCarney     }
951*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
952*0e8c68abSShawn McCarney     {
953*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
954*0e8c68abSShawn McCarney     }
955*0e8c68abSShawn McCarney }
956*0e8c68abSShawn McCarney 
957*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, ParseString)
958*0e8c68abSShawn McCarney {
959*0e8c68abSShawn McCarney     // Test where works: Empty string
960*0e8c68abSShawn McCarney     {
961*0e8c68abSShawn McCarney         const json element = "";
962*0e8c68abSShawn McCarney         std::string value = parseString(element, true);
963*0e8c68abSShawn McCarney         EXPECT_EQ(value, "");
964*0e8c68abSShawn McCarney     }
965*0e8c68abSShawn McCarney 
966*0e8c68abSShawn McCarney     // Test where works: Non-empty string
967*0e8c68abSShawn McCarney     {
968*0e8c68abSShawn McCarney         const json element = "vdd_regulator";
969*0e8c68abSShawn McCarney         std::string value = parseString(element, false);
970*0e8c68abSShawn McCarney         EXPECT_EQ(value, "vdd_regulator");
971*0e8c68abSShawn McCarney     }
972*0e8c68abSShawn McCarney 
973*0e8c68abSShawn McCarney     // Test where fails: Element is not a string
974*0e8c68abSShawn McCarney     try
975*0e8c68abSShawn McCarney     {
976*0e8c68abSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
977*0e8c68abSShawn McCarney         parseString(element);
978*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
979*0e8c68abSShawn McCarney     }
980*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
981*0e8c68abSShawn McCarney     {
982*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a string");
983*0e8c68abSShawn McCarney     }
984*0e8c68abSShawn McCarney 
985*0e8c68abSShawn McCarney     // Test where fails: Empty string
986*0e8c68abSShawn McCarney     try
987*0e8c68abSShawn McCarney     {
988*0e8c68abSShawn McCarney         const json element = "";
989*0e8c68abSShawn McCarney         parseString(element);
990*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
991*0e8c68abSShawn McCarney     }
992*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
993*0e8c68abSShawn McCarney     {
994*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an empty string");
995*0e8c68abSShawn McCarney     }
996*0e8c68abSShawn McCarney }
997*0e8c68abSShawn McCarney 
998*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, VerifyIsArray)
999*0e8c68abSShawn McCarney {
1000*0e8c68abSShawn McCarney     // Test where element is an array
1001*0e8c68abSShawn McCarney     try
1002*0e8c68abSShawn McCarney     {
1003*0e8c68abSShawn McCarney         const json element = R"( [ "foo", "bar" ] )"_json;
1004*0e8c68abSShawn McCarney         verifyIsArray(element);
1005*0e8c68abSShawn McCarney     }
1006*0e8c68abSShawn McCarney     catch (const std::exception& e)
1007*0e8c68abSShawn McCarney     {
1008*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have caught exception.";
1009*0e8c68abSShawn McCarney     }
1010*0e8c68abSShawn McCarney 
1011*0e8c68abSShawn McCarney     // Test where element is not an array
1012*0e8c68abSShawn McCarney     try
1013*0e8c68abSShawn McCarney     {
1014*0e8c68abSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
1015*0e8c68abSShawn McCarney         verifyIsArray(element);
1016*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
1017*0e8c68abSShawn McCarney     }
1018*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
1019*0e8c68abSShawn McCarney     {
1020*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
1021*0e8c68abSShawn McCarney     }
1022*0e8c68abSShawn McCarney }
1023*0e8c68abSShawn McCarney 
1024*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, VerifyIsObject)
1025*0e8c68abSShawn McCarney {
1026*0e8c68abSShawn McCarney     // Test where element is an object
1027*0e8c68abSShawn McCarney     try
1028*0e8c68abSShawn McCarney     {
1029*0e8c68abSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
1030*0e8c68abSShawn McCarney         verifyIsObject(element);
1031*0e8c68abSShawn McCarney     }
1032*0e8c68abSShawn McCarney     catch (const std::exception& e)
1033*0e8c68abSShawn McCarney     {
1034*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have caught exception.";
1035*0e8c68abSShawn McCarney     }
1036*0e8c68abSShawn McCarney 
1037*0e8c68abSShawn McCarney     // Test where element is not an object
1038*0e8c68abSShawn McCarney     try
1039*0e8c68abSShawn McCarney     {
1040*0e8c68abSShawn McCarney         const json element = R"( [ "foo", "bar" ] )"_json;
1041*0e8c68abSShawn McCarney         verifyIsObject(element);
1042*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
1043*0e8c68abSShawn McCarney     }
1044*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
1045*0e8c68abSShawn McCarney     {
1046*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
1047*0e8c68abSShawn McCarney     }
1048*0e8c68abSShawn McCarney }
1049*0e8c68abSShawn McCarney 
1050*0e8c68abSShawn McCarney TEST(ConfigFileParserTests, VerifyPropertyCount)
1051*0e8c68abSShawn McCarney {
1052*0e8c68abSShawn McCarney     // Test where element has expected number of properties
1053*0e8c68abSShawn McCarney     try
1054*0e8c68abSShawn McCarney     {
1055*0e8c68abSShawn McCarney         const json element = R"(
1056*0e8c68abSShawn McCarney             {
1057*0e8c68abSShawn McCarney               "comments": [ "Set voltage rule" ],
1058*0e8c68abSShawn McCarney               "id": "set_voltage_rule"
1059*0e8c68abSShawn McCarney             }
1060*0e8c68abSShawn McCarney         )"_json;
1061*0e8c68abSShawn McCarney         verifyPropertyCount(element, 2);
1062*0e8c68abSShawn McCarney     }
1063*0e8c68abSShawn McCarney     catch (const std::exception& e)
1064*0e8c68abSShawn McCarney     {
1065*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have caught exception.";
1066*0e8c68abSShawn McCarney     }
1067*0e8c68abSShawn McCarney 
1068*0e8c68abSShawn McCarney     // Test where element has unexpected number of properties
1069*0e8c68abSShawn McCarney     try
1070*0e8c68abSShawn McCarney     {
1071*0e8c68abSShawn McCarney         const json element = R"(
1072*0e8c68abSShawn McCarney             {
1073*0e8c68abSShawn McCarney               "comments": [ "Set voltage rule" ],
1074*0e8c68abSShawn McCarney               "id": "set_voltage_rule",
1075*0e8c68abSShawn McCarney               "foo": 1.3
1076*0e8c68abSShawn McCarney             }
1077*0e8c68abSShawn McCarney         )"_json;
1078*0e8c68abSShawn McCarney         verifyPropertyCount(element, 2);
1079*0e8c68abSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
1080*0e8c68abSShawn McCarney     }
1081*0e8c68abSShawn McCarney     catch (const std::invalid_argument& e)
1082*0e8c68abSShawn McCarney     {
1083*0e8c68abSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
1084*0e8c68abSShawn McCarney     }
1085*0e8c68abSShawn McCarney }
1086