16a957f6fSShawn McCarney /**
26a957f6fSShawn McCarney  * Copyright © 2024 IBM Corporation
36a957f6fSShawn McCarney  *
46a957f6fSShawn McCarney  * Licensed under the Apache License, Version 2.0 (the "License");
56a957f6fSShawn McCarney  * you may not use this file except in compliance with the License.
66a957f6fSShawn McCarney  * You may obtain a copy of the License at
76a957f6fSShawn McCarney  *
86a957f6fSShawn McCarney  *     http://www.apache.org/licenses/LICENSE-2.0
96a957f6fSShawn McCarney  *
106a957f6fSShawn McCarney  * Unless required by applicable law or agreed to in writing, software
116a957f6fSShawn McCarney  * distributed under the License is distributed on an "AS IS" BASIS,
126a957f6fSShawn McCarney  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a957f6fSShawn McCarney  * See the License for the specific language governing permissions and
146a957f6fSShawn McCarney  * limitations under the License.
156a957f6fSShawn McCarney  */
166a957f6fSShawn McCarney #include "config_file_parser.hpp"
176a957f6fSShawn McCarney #include "config_file_parser_error.hpp"
186a957f6fSShawn McCarney #include "rail.hpp"
196a957f6fSShawn McCarney #include "temporary_file.hpp"
20*e9144ab4SShawn McCarney #include "temporary_subdirectory.hpp"
216a957f6fSShawn McCarney 
226a957f6fSShawn McCarney #include <sys/stat.h> // for chmod()
236a957f6fSShawn McCarney 
246a957f6fSShawn McCarney #include <nlohmann/json.hpp>
256a957f6fSShawn McCarney 
266a957f6fSShawn McCarney #include <cstdint>
276a957f6fSShawn McCarney #include <exception>
286a957f6fSShawn McCarney #include <filesystem>
296a957f6fSShawn McCarney #include <fstream>
306a957f6fSShawn McCarney #include <memory>
316a957f6fSShawn McCarney #include <optional>
326a957f6fSShawn McCarney #include <stdexcept>
336a957f6fSShawn McCarney #include <string>
346a957f6fSShawn McCarney #include <vector>
356a957f6fSShawn McCarney 
366a957f6fSShawn McCarney #include <gtest/gtest.h>
376a957f6fSShawn McCarney 
386a957f6fSShawn McCarney using namespace phosphor::power::sequencer;
396a957f6fSShawn McCarney using namespace phosphor::power::sequencer::config_file_parser;
406a957f6fSShawn McCarney using namespace phosphor::power::sequencer::config_file_parser::internal;
41*e9144ab4SShawn McCarney using namespace phosphor::power::util;
426a957f6fSShawn McCarney using json = nlohmann::json;
43*e9144ab4SShawn McCarney namespace fs = std::filesystem;
446a957f6fSShawn McCarney 
writeConfigFile(const fs::path & pathName,const std::string & contents)45*e9144ab4SShawn McCarney void writeConfigFile(const fs::path& pathName, const std::string& contents)
466a957f6fSShawn McCarney {
476a957f6fSShawn McCarney     std::ofstream file{pathName};
486a957f6fSShawn McCarney     file << contents;
496a957f6fSShawn McCarney }
506a957f6fSShawn McCarney 
writeConfigFile(const fs::path & pathName,const json & contents)51*e9144ab4SShawn McCarney void writeConfigFile(const fs::path& pathName, const json& contents)
526a957f6fSShawn McCarney {
536a957f6fSShawn McCarney     std::ofstream file{pathName};
546a957f6fSShawn McCarney     file << contents;
556a957f6fSShawn McCarney }
566a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,Find)57*e9144ab4SShawn McCarney TEST(ConfigFileParserTests, Find)
58*e9144ab4SShawn McCarney {
59*e9144ab4SShawn McCarney     std::vector<std::string> compatibleSystemTypes{
60*e9144ab4SShawn McCarney         "com.acme.Hardware.Chassis.Model.MegaServer4CPU",
61*e9144ab4SShawn McCarney         "com.acme.Hardware.Chassis.Model.MegaServer",
62*e9144ab4SShawn McCarney         "com.acme.Hardware.Chassis.Model.Server"};
63*e9144ab4SShawn McCarney 
64*e9144ab4SShawn McCarney     // Test where works: Fully qualified system type: First in list
65*e9144ab4SShawn McCarney     {
66*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
67*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
68*e9144ab4SShawn McCarney 
69*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
70*e9144ab4SShawn McCarney         configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
71*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
72*e9144ab4SShawn McCarney 
73*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
74*e9144ab4SShawn McCarney         EXPECT_EQ(pathFound, configFilePath);
75*e9144ab4SShawn McCarney     }
76*e9144ab4SShawn McCarney 
77*e9144ab4SShawn McCarney     // Test where works: Fully qualified system type: Second in list
78*e9144ab4SShawn McCarney     {
79*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
80*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
81*e9144ab4SShawn McCarney 
82*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
83*e9144ab4SShawn McCarney         configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer.json";
84*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
85*e9144ab4SShawn McCarney 
86*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
87*e9144ab4SShawn McCarney         EXPECT_EQ(pathFound, configFilePath);
88*e9144ab4SShawn McCarney     }
89*e9144ab4SShawn McCarney 
90*e9144ab4SShawn McCarney     // Test where works: Last node in system type: Second in list
91*e9144ab4SShawn McCarney     {
92*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
93*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
94*e9144ab4SShawn McCarney 
95*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
96*e9144ab4SShawn McCarney         configFilePath /= "MegaServer.json";
97*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
98*e9144ab4SShawn McCarney 
99*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
100*e9144ab4SShawn McCarney         EXPECT_EQ(pathFound, configFilePath);
101*e9144ab4SShawn McCarney     }
102*e9144ab4SShawn McCarney 
103*e9144ab4SShawn McCarney     // Test where works: Last node in system type: Last in list
104*e9144ab4SShawn McCarney     {
105*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
106*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
107*e9144ab4SShawn McCarney 
108*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
109*e9144ab4SShawn McCarney         configFilePath /= "Server.json";
110*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
111*e9144ab4SShawn McCarney 
112*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
113*e9144ab4SShawn McCarney         EXPECT_EQ(pathFound, configFilePath);
114*e9144ab4SShawn McCarney     }
115*e9144ab4SShawn McCarney 
116*e9144ab4SShawn McCarney     // Test where works: System type has no '.'
117*e9144ab4SShawn McCarney     {
118*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
119*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
120*e9144ab4SShawn McCarney 
121*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
122*e9144ab4SShawn McCarney         configFilePath /= "Server.json";
123*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
124*e9144ab4SShawn McCarney 
125*e9144ab4SShawn McCarney         std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
126*e9144ab4SShawn McCarney                                                   "MegaServer", "Server"};
127*e9144ab4SShawn McCarney         fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
128*e9144ab4SShawn McCarney         EXPECT_EQ(pathFound, configFilePath);
129*e9144ab4SShawn McCarney     }
130*e9144ab4SShawn McCarney 
131*e9144ab4SShawn McCarney     // Test where fails: System type list is empty
132*e9144ab4SShawn McCarney     {
133*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
134*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
135*e9144ab4SShawn McCarney 
136*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
137*e9144ab4SShawn McCarney         configFilePath /= "Server.json";
138*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
139*e9144ab4SShawn McCarney 
140*e9144ab4SShawn McCarney         std::vector<std::string> emptySystemTypes{};
141*e9144ab4SShawn McCarney         fs::path pathFound = find(emptySystemTypes, configFileDirPath);
142*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
143*e9144ab4SShawn McCarney     }
144*e9144ab4SShawn McCarney 
145*e9144ab4SShawn McCarney     // Test where fails: Configuration file directory is empty
146*e9144ab4SShawn McCarney     {
147*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
148*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
149*e9144ab4SShawn McCarney 
150*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
151*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
152*e9144ab4SShawn McCarney     }
153*e9144ab4SShawn McCarney 
154*e9144ab4SShawn McCarney     // Test where fails: Configuration file directory does not exist
155*e9144ab4SShawn McCarney     {
156*e9144ab4SShawn McCarney         fs::path configFileDirPath{"/tmp/does_not_exist_XYZ"};
157*e9144ab4SShawn McCarney 
158*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
159*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
160*e9144ab4SShawn McCarney     }
161*e9144ab4SShawn McCarney 
162*e9144ab4SShawn McCarney     // Test where fails: Configuration file directory is not readable
163*e9144ab4SShawn McCarney     {
164*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
165*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
166*e9144ab4SShawn McCarney         fs::permissions(configFileDirPath, fs::perms::none);
167*e9144ab4SShawn McCarney 
168*e9144ab4SShawn McCarney         EXPECT_THROW(find(compatibleSystemTypes, configFileDirPath),
169*e9144ab4SShawn McCarney                      std::exception);
170*e9144ab4SShawn McCarney 
171*e9144ab4SShawn McCarney         fs::permissions(configFileDirPath, fs::perms::owner_all);
172*e9144ab4SShawn McCarney     }
173*e9144ab4SShawn McCarney 
174*e9144ab4SShawn McCarney     // Test where fails: No matching file name found
175*e9144ab4SShawn McCarney     {
176*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
177*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
178*e9144ab4SShawn McCarney 
179*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
180*e9144ab4SShawn McCarney         configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer";
181*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
182*e9144ab4SShawn McCarney 
183*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
184*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
185*e9144ab4SShawn McCarney     }
186*e9144ab4SShawn McCarney 
187*e9144ab4SShawn McCarney     // Test where fails: Matching file name is a directory: Fully qualified
188*e9144ab4SShawn McCarney     {
189*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
190*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
191*e9144ab4SShawn McCarney 
192*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
193*e9144ab4SShawn McCarney         configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json";
194*e9144ab4SShawn McCarney         fs::create_directory(configFilePath);
195*e9144ab4SShawn McCarney 
196*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
197*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
198*e9144ab4SShawn McCarney     }
199*e9144ab4SShawn McCarney 
200*e9144ab4SShawn McCarney     // Test where fails: Matching file name is a directory: Last node
201*e9144ab4SShawn McCarney     {
202*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
203*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
204*e9144ab4SShawn McCarney 
205*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
206*e9144ab4SShawn McCarney         configFilePath /= "MegaServer.json";
207*e9144ab4SShawn McCarney         fs::create_directory(configFilePath);
208*e9144ab4SShawn McCarney 
209*e9144ab4SShawn McCarney         fs::path pathFound = find(compatibleSystemTypes, configFileDirPath);
210*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
211*e9144ab4SShawn McCarney     }
212*e9144ab4SShawn McCarney 
213*e9144ab4SShawn McCarney     // Test where fails: System type has no '.'
214*e9144ab4SShawn McCarney     {
215*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
216*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
217*e9144ab4SShawn McCarney 
218*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
219*e9144ab4SShawn McCarney         configFilePath /= "MegaServer2CPU.json";
220*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
221*e9144ab4SShawn McCarney 
222*e9144ab4SShawn McCarney         std::vector<std::string> noDotSystemTypes{"MegaServer4CPU",
223*e9144ab4SShawn McCarney                                                   "MegaServer", "Server", ""};
224*e9144ab4SShawn McCarney         fs::path pathFound = find(noDotSystemTypes, configFileDirPath);
225*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
226*e9144ab4SShawn McCarney     }
227*e9144ab4SShawn McCarney 
228*e9144ab4SShawn McCarney     // Test where fails: System type ends with '.'
229*e9144ab4SShawn McCarney     {
230*e9144ab4SShawn McCarney         TemporarySubDirectory configFileDir;
231*e9144ab4SShawn McCarney         fs::path configFileDirPath = configFileDir.getPath();
232*e9144ab4SShawn McCarney 
233*e9144ab4SShawn McCarney         fs::path configFilePath = configFileDirPath;
234*e9144ab4SShawn McCarney         configFilePath /= "MegaServer4CPU.json";
235*e9144ab4SShawn McCarney         writeConfigFile(configFilePath, std::string{""});
236*e9144ab4SShawn McCarney 
237*e9144ab4SShawn McCarney         std::vector<std::string> dotAtEndSystemTypes{
238*e9144ab4SShawn McCarney             "com.acme.Hardware.Chassis.Model.MegaServer4CPU.", "a.", "."};
239*e9144ab4SShawn McCarney         fs::path pathFound = find(dotAtEndSystemTypes, configFileDirPath);
240*e9144ab4SShawn McCarney         EXPECT_TRUE(pathFound.empty());
241*e9144ab4SShawn McCarney     }
242*e9144ab4SShawn McCarney }
243*e9144ab4SShawn McCarney 
TEST(ConfigFileParserTests,Parse)2446a957f6fSShawn McCarney TEST(ConfigFileParserTests, Parse)
2456a957f6fSShawn McCarney {
2466a957f6fSShawn McCarney     // Test where works
2476a957f6fSShawn McCarney     {
2486a957f6fSShawn McCarney         const json configFileContents = R"(
2496a957f6fSShawn McCarney             {
2506a957f6fSShawn McCarney                 "rails": [
2516a957f6fSShawn McCarney                     {
2526a957f6fSShawn McCarney                         "name": "VDD_CPU0",
2536a957f6fSShawn McCarney                         "page": 11,
2546a957f6fSShawn McCarney                         "check_status_vout": true
2556a957f6fSShawn McCarney                     },
2566a957f6fSShawn McCarney                     {
2576a957f6fSShawn McCarney                         "name": "VCS_CPU1",
2586a957f6fSShawn McCarney                         "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
2596a957f6fSShawn McCarney                         "gpio": { "line": 60 }
2606a957f6fSShawn McCarney                     }
2616a957f6fSShawn McCarney                 ]
2626a957f6fSShawn McCarney             }
2636a957f6fSShawn McCarney         )"_json;
2646a957f6fSShawn McCarney 
2656a957f6fSShawn McCarney         TemporaryFile configFile;
266*e9144ab4SShawn McCarney         fs::path pathName{configFile.getPath()};
2676a957f6fSShawn McCarney         writeConfigFile(pathName, configFileContents);
2686a957f6fSShawn McCarney 
2696a957f6fSShawn McCarney         std::vector<std::unique_ptr<Rail>> rails = parse(pathName);
2706a957f6fSShawn McCarney 
2716a957f6fSShawn McCarney         EXPECT_EQ(rails.size(), 2);
2726a957f6fSShawn McCarney         EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
2736a957f6fSShawn McCarney         EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
2746a957f6fSShawn McCarney     }
2756a957f6fSShawn McCarney 
2766a957f6fSShawn McCarney     // Test where fails: File does not exist
2776a957f6fSShawn McCarney     {
278*e9144ab4SShawn McCarney         fs::path pathName{"/tmp/non_existent_file"};
2796a957f6fSShawn McCarney         EXPECT_THROW(parse(pathName), ConfigFileParserError);
2806a957f6fSShawn McCarney     }
2816a957f6fSShawn McCarney 
2826a957f6fSShawn McCarney     // Test where fails: File is not readable
2836a957f6fSShawn McCarney     {
2846a957f6fSShawn McCarney         const json configFileContents = R"(
2856a957f6fSShawn McCarney             {
2866a957f6fSShawn McCarney                 "rails": [
2876a957f6fSShawn McCarney                     {
2886a957f6fSShawn McCarney                         "name": "VDD_CPU0"
2896a957f6fSShawn McCarney                     }
2906a957f6fSShawn McCarney                 ]
2916a957f6fSShawn McCarney             }
2926a957f6fSShawn McCarney         )"_json;
2936a957f6fSShawn McCarney 
2946a957f6fSShawn McCarney         TemporaryFile configFile;
295*e9144ab4SShawn McCarney         fs::path pathName{configFile.getPath()};
2966a957f6fSShawn McCarney         writeConfigFile(pathName, configFileContents);
2976a957f6fSShawn McCarney 
2986a957f6fSShawn McCarney         chmod(pathName.c_str(), 0222);
2996a957f6fSShawn McCarney         EXPECT_THROW(parse(pathName), ConfigFileParserError);
3006a957f6fSShawn McCarney     }
3016a957f6fSShawn McCarney 
3026a957f6fSShawn McCarney     // Test where fails: File is not valid JSON
3036a957f6fSShawn McCarney     {
3046a957f6fSShawn McCarney         const std::string configFileContents = "] foo [";
3056a957f6fSShawn McCarney 
3066a957f6fSShawn McCarney         TemporaryFile configFile;
307*e9144ab4SShawn McCarney         fs::path pathName{configFile.getPath()};
3086a957f6fSShawn McCarney         writeConfigFile(pathName, configFileContents);
3096a957f6fSShawn McCarney 
3106a957f6fSShawn McCarney         EXPECT_THROW(parse(pathName), ConfigFileParserError);
3116a957f6fSShawn McCarney     }
3126a957f6fSShawn McCarney 
3136a957f6fSShawn McCarney     // Test where fails: JSON does not conform to config file format
3146a957f6fSShawn McCarney     {
3156a957f6fSShawn McCarney         const json configFileContents = R"( [ "foo", "bar" ] )"_json;
3166a957f6fSShawn McCarney 
3176a957f6fSShawn McCarney         TemporaryFile configFile;
318*e9144ab4SShawn McCarney         fs::path pathName{configFile.getPath()};
3196a957f6fSShawn McCarney         writeConfigFile(pathName, configFileContents);
3206a957f6fSShawn McCarney 
3216a957f6fSShawn McCarney         EXPECT_THROW(parse(pathName), ConfigFileParserError);
3226a957f6fSShawn McCarney     }
3236a957f6fSShawn McCarney }
3246a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,GetRequiredProperty)3256a957f6fSShawn McCarney TEST(ConfigFileParserTests, GetRequiredProperty)
3266a957f6fSShawn McCarney {
3276a957f6fSShawn McCarney     // Test where property exists
3286a957f6fSShawn McCarney     {
3296a957f6fSShawn McCarney         const json element = R"( { "name": "VDD_CPU0" } )"_json;
3306a957f6fSShawn McCarney         const json& propertyElement = getRequiredProperty(element, "name");
3316a957f6fSShawn McCarney         EXPECT_EQ(propertyElement.get<std::string>(), "VDD_CPU0");
3326a957f6fSShawn McCarney     }
3336a957f6fSShawn McCarney 
3346a957f6fSShawn McCarney     // Test where property does not exist
3356a957f6fSShawn McCarney     try
3366a957f6fSShawn McCarney     {
3376a957f6fSShawn McCarney         const json element = R"( { "foo": 23 } )"_json;
3386a957f6fSShawn McCarney         getRequiredProperty(element, "name");
3396a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
3406a957f6fSShawn McCarney     }
3416a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
3426a957f6fSShawn McCarney     {
3436a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: name");
3446a957f6fSShawn McCarney     }
3456a957f6fSShawn McCarney }
3466a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseBoolean)3476a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseBoolean)
3486a957f6fSShawn McCarney {
3496a957f6fSShawn McCarney     // Test where works: true
3506a957f6fSShawn McCarney     {
3516a957f6fSShawn McCarney         const json element = R"( true )"_json;
3526a957f6fSShawn McCarney         bool value = parseBoolean(element);
3536a957f6fSShawn McCarney         EXPECT_EQ(value, true);
3546a957f6fSShawn McCarney     }
3556a957f6fSShawn McCarney 
3566a957f6fSShawn McCarney     // Test where works: false
3576a957f6fSShawn McCarney     {
3586a957f6fSShawn McCarney         const json element = R"( false )"_json;
3596a957f6fSShawn McCarney         bool value = parseBoolean(element);
3606a957f6fSShawn McCarney         EXPECT_EQ(value, false);
3616a957f6fSShawn McCarney     }
3626a957f6fSShawn McCarney 
3636a957f6fSShawn McCarney     // Test where fails: Element is not a boolean
3646a957f6fSShawn McCarney     try
3656a957f6fSShawn McCarney     {
3666a957f6fSShawn McCarney         const json element = R"( 1 )"_json;
3676a957f6fSShawn McCarney         parseBoolean(element);
3686a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
3696a957f6fSShawn McCarney     }
3706a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
3716a957f6fSShawn McCarney     {
3726a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
3736a957f6fSShawn McCarney     }
3746a957f6fSShawn McCarney }
3756a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseGPIO)3766a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseGPIO)
3776a957f6fSShawn McCarney {
3786a957f6fSShawn McCarney     // Test where works: Only required properties specified
3796a957f6fSShawn McCarney     {
3806a957f6fSShawn McCarney         const json element = R"(
3816a957f6fSShawn McCarney             {
3826a957f6fSShawn McCarney                 "line": 60
3836a957f6fSShawn McCarney             }
3846a957f6fSShawn McCarney         )"_json;
3856a957f6fSShawn McCarney         GPIO gpio = parseGPIO(element);
3866a957f6fSShawn McCarney         EXPECT_EQ(gpio.line, 60);
3876a957f6fSShawn McCarney         EXPECT_FALSE(gpio.activeLow);
3886a957f6fSShawn McCarney     }
3896a957f6fSShawn McCarney 
3906a957f6fSShawn McCarney     // Test where works: All properties specified
3916a957f6fSShawn McCarney     {
3926a957f6fSShawn McCarney         const json element = R"(
3936a957f6fSShawn McCarney             {
3946a957f6fSShawn McCarney                 "line": 131,
3956a957f6fSShawn McCarney                 "active_low": true
3966a957f6fSShawn McCarney             }
3976a957f6fSShawn McCarney         )"_json;
3986a957f6fSShawn McCarney         GPIO gpio = parseGPIO(element);
3996a957f6fSShawn McCarney         EXPECT_EQ(gpio.line, 131);
4006a957f6fSShawn McCarney         EXPECT_TRUE(gpio.activeLow);
4016a957f6fSShawn McCarney     }
4026a957f6fSShawn McCarney 
4036a957f6fSShawn McCarney     // Test where fails: Element is not an object
4046a957f6fSShawn McCarney     try
4056a957f6fSShawn McCarney     {
4066a957f6fSShawn McCarney         const json element = R"( [ "vdda", "vddb" ] )"_json;
4076a957f6fSShawn McCarney         parseGPIO(element);
4086a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
4096a957f6fSShawn McCarney     }
4106a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
4116a957f6fSShawn McCarney     {
4126a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
4136a957f6fSShawn McCarney     }
4146a957f6fSShawn McCarney 
4156a957f6fSShawn McCarney     // Test where fails: Required line property not specified
4166a957f6fSShawn McCarney     try
4176a957f6fSShawn McCarney     {
4186a957f6fSShawn McCarney         const json element = R"(
4196a957f6fSShawn McCarney             {
4206a957f6fSShawn McCarney                 "active_low": true
4216a957f6fSShawn McCarney             }
4226a957f6fSShawn McCarney         )"_json;
4236a957f6fSShawn McCarney         parseGPIO(element);
4246a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
4256a957f6fSShawn McCarney     }
4266a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
4276a957f6fSShawn McCarney     {
4286a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: line");
4296a957f6fSShawn McCarney     }
4306a957f6fSShawn McCarney 
4316a957f6fSShawn McCarney     // Test where fails: line value is invalid
4326a957f6fSShawn McCarney     try
4336a957f6fSShawn McCarney     {
4346a957f6fSShawn McCarney         const json element = R"(
4356a957f6fSShawn McCarney             {
4366a957f6fSShawn McCarney                 "line": -131,
4376a957f6fSShawn McCarney                 "active_low": true
4386a957f6fSShawn McCarney             }
4396a957f6fSShawn McCarney         )"_json;
4406a957f6fSShawn McCarney         parseGPIO(element);
4416a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
4426a957f6fSShawn McCarney     }
4436a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
4446a957f6fSShawn McCarney     {
4456a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
4466a957f6fSShawn McCarney     }
4476a957f6fSShawn McCarney 
4486a957f6fSShawn McCarney     // Test where fails: active_low value is invalid
4496a957f6fSShawn McCarney     try
4506a957f6fSShawn McCarney     {
4516a957f6fSShawn McCarney         const json element = R"(
4526a957f6fSShawn McCarney             {
4536a957f6fSShawn McCarney                 "line": 131,
4546a957f6fSShawn McCarney                 "active_low": "true"
4556a957f6fSShawn McCarney             }
4566a957f6fSShawn McCarney         )"_json;
4576a957f6fSShawn McCarney         parseGPIO(element);
4586a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
4596a957f6fSShawn McCarney     }
4606a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
4616a957f6fSShawn McCarney     {
4626a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
4636a957f6fSShawn McCarney     }
4646a957f6fSShawn McCarney 
4656a957f6fSShawn McCarney     // Test where fails: Invalid property specified
4666a957f6fSShawn McCarney     try
4676a957f6fSShawn McCarney     {
4686a957f6fSShawn McCarney         const json element = R"(
4696a957f6fSShawn McCarney             {
4706a957f6fSShawn McCarney                 "line": 131,
4716a957f6fSShawn McCarney                 "foo": "bar"
4726a957f6fSShawn McCarney             }
4736a957f6fSShawn McCarney         )"_json;
4746a957f6fSShawn McCarney         parseGPIO(element);
4756a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
4766a957f6fSShawn McCarney     }
4776a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
4786a957f6fSShawn McCarney     {
4796a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
4806a957f6fSShawn McCarney     }
4816a957f6fSShawn McCarney }
4826a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseRail)4836a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseRail)
4846a957f6fSShawn McCarney {
4856a957f6fSShawn McCarney     // Test where works: Only required properties specified
4866a957f6fSShawn McCarney     {
4876a957f6fSShawn McCarney         const json element = R"(
4886a957f6fSShawn McCarney             {
4896a957f6fSShawn McCarney                 "name": "VDD_CPU0"
4906a957f6fSShawn McCarney             }
4916a957f6fSShawn McCarney         )"_json;
4926a957f6fSShawn McCarney         std::unique_ptr<Rail> rail = parseRail(element);
4936a957f6fSShawn McCarney         EXPECT_EQ(rail->getName(), "VDD_CPU0");
4946a957f6fSShawn McCarney         EXPECT_FALSE(rail->getPresence().has_value());
4956a957f6fSShawn McCarney         EXPECT_FALSE(rail->getPage().has_value());
49616e493afSShawn McCarney         EXPECT_FALSE(rail->isPowerSupplyRail());
4976a957f6fSShawn McCarney         EXPECT_FALSE(rail->getCheckStatusVout());
4989ec0d43dSShawn McCarney         EXPECT_FALSE(rail->getCompareVoltageToLimit());
4996a957f6fSShawn McCarney         EXPECT_FALSE(rail->getGPIO().has_value());
5006a957f6fSShawn McCarney     }
5016a957f6fSShawn McCarney 
5026a957f6fSShawn McCarney     // Test where works: All properties specified
5036a957f6fSShawn McCarney     {
5046a957f6fSShawn McCarney         const json element = R"(
5056a957f6fSShawn McCarney             {
50616e493afSShawn McCarney                 "name": "12.0VB",
50716e493afSShawn McCarney                 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply1",
5086a957f6fSShawn McCarney                 "page": 11,
50916e493afSShawn McCarney                 "is_power_supply_rail": true,
5106a957f6fSShawn McCarney                 "check_status_vout": true,
5119ec0d43dSShawn McCarney                 "compare_voltage_to_limit": true,
5126a957f6fSShawn McCarney                 "gpio": { "line": 60, "active_low": true }
5136a957f6fSShawn McCarney             }
5146a957f6fSShawn McCarney         )"_json;
5156a957f6fSShawn McCarney         std::unique_ptr<Rail> rail = parseRail(element);
51616e493afSShawn McCarney         EXPECT_EQ(rail->getName(), "12.0VB");
5176a957f6fSShawn McCarney         EXPECT_TRUE(rail->getPresence().has_value());
51816e493afSShawn McCarney         EXPECT_EQ(rail->getPresence().value(),
51916e493afSShawn McCarney                   "/xyz/openbmc_project/inventory/system/chassis/powersupply1");
5206a957f6fSShawn McCarney         EXPECT_TRUE(rail->getPage().has_value());
5216a957f6fSShawn McCarney         EXPECT_EQ(rail->getPage().value(), 11);
52216e493afSShawn McCarney         EXPECT_TRUE(rail->isPowerSupplyRail());
5236a957f6fSShawn McCarney         EXPECT_TRUE(rail->getCheckStatusVout());
5249ec0d43dSShawn McCarney         EXPECT_TRUE(rail->getCompareVoltageToLimit());
5256a957f6fSShawn McCarney         EXPECT_TRUE(rail->getGPIO().has_value());
5266a957f6fSShawn McCarney         EXPECT_EQ(rail->getGPIO().value().line, 60);
5276a957f6fSShawn McCarney         EXPECT_TRUE(rail->getGPIO().value().activeLow);
5286a957f6fSShawn McCarney     }
5296a957f6fSShawn McCarney 
5306a957f6fSShawn McCarney     // Test where fails: Element is not an object
5316a957f6fSShawn McCarney     try
5326a957f6fSShawn McCarney     {
5336a957f6fSShawn McCarney         const json element = R"( [ "vdda", "vddb" ] )"_json;
5346a957f6fSShawn McCarney         parseRail(element);
5356a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
5366a957f6fSShawn McCarney     }
5376a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
5386a957f6fSShawn McCarney     {
5396a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
5406a957f6fSShawn McCarney     }
5416a957f6fSShawn McCarney 
5426a957f6fSShawn McCarney     // Test where fails: Required name property not specified
5436a957f6fSShawn McCarney     try
5446a957f6fSShawn McCarney     {
5456a957f6fSShawn McCarney         const json element = R"(
5466a957f6fSShawn McCarney             {
5476a957f6fSShawn McCarney                 "page": 11
5486a957f6fSShawn McCarney             }
5496a957f6fSShawn McCarney         )"_json;
5506a957f6fSShawn McCarney         parseRail(element);
5516a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
5526a957f6fSShawn McCarney     }
5536a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
5546a957f6fSShawn McCarney     {
5556a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: name");
5566a957f6fSShawn McCarney     }
5576a957f6fSShawn McCarney 
5586a957f6fSShawn McCarney     // Test where fails: name value is invalid
5596a957f6fSShawn McCarney     try
5606a957f6fSShawn McCarney     {
5616a957f6fSShawn McCarney         const json element = R"(
5626a957f6fSShawn McCarney             {
5636a957f6fSShawn McCarney                 "name": 31,
5646a957f6fSShawn McCarney                 "page": 11
5656a957f6fSShawn McCarney             }
5666a957f6fSShawn McCarney         )"_json;
5676a957f6fSShawn McCarney         parseRail(element);
5686a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
5696a957f6fSShawn McCarney     }
5706a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
5716a957f6fSShawn McCarney     {
5726a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a string");
5736a957f6fSShawn McCarney     }
5746a957f6fSShawn McCarney 
5756a957f6fSShawn McCarney     // Test where fails: presence value is invalid
5766a957f6fSShawn McCarney     try
5776a957f6fSShawn McCarney     {
5786a957f6fSShawn McCarney         const json element = R"(
5796a957f6fSShawn McCarney             {
5806a957f6fSShawn McCarney                 "name": "VCS_CPU1",
5816a957f6fSShawn McCarney                 "presence": false
5826a957f6fSShawn McCarney             }
5836a957f6fSShawn McCarney         )"_json;
5846a957f6fSShawn McCarney         parseRail(element);
5856a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
5866a957f6fSShawn McCarney     }
5876a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
5886a957f6fSShawn McCarney     {
5896a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a string");
5906a957f6fSShawn McCarney     }
5916a957f6fSShawn McCarney 
5926a957f6fSShawn McCarney     // Test where fails: page value is invalid
5936a957f6fSShawn McCarney     try
5946a957f6fSShawn McCarney     {
5956a957f6fSShawn McCarney         const json element = R"(
5966a957f6fSShawn McCarney             {
5976a957f6fSShawn McCarney                 "name": "VCS_CPU1",
5986a957f6fSShawn McCarney                 "page": 256
5996a957f6fSShawn McCarney             }
6006a957f6fSShawn McCarney         )"_json;
6016a957f6fSShawn McCarney         parseRail(element);
6026a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
6036a957f6fSShawn McCarney     }
6046a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
6056a957f6fSShawn McCarney     {
6066a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
6076a957f6fSShawn McCarney     }
6086a957f6fSShawn McCarney 
60916e493afSShawn McCarney     // Test where fails: is_power_supply_rail value is invalid
61016e493afSShawn McCarney     try
61116e493afSShawn McCarney     {
61216e493afSShawn McCarney         const json element = R"(
61316e493afSShawn McCarney             {
61416e493afSShawn McCarney                 "name": "12.0VA",
61516e493afSShawn McCarney                 "is_power_supply_rail": "true"
61616e493afSShawn McCarney             }
61716e493afSShawn McCarney         )"_json;
61816e493afSShawn McCarney         parseRail(element);
61916e493afSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
62016e493afSShawn McCarney     }
62116e493afSShawn McCarney     catch (const std::invalid_argument& e)
62216e493afSShawn McCarney     {
62316e493afSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
62416e493afSShawn McCarney     }
62516e493afSShawn McCarney 
6266a957f6fSShawn McCarney     // Test where fails: check_status_vout value is invalid
6276a957f6fSShawn McCarney     try
6286a957f6fSShawn McCarney     {
6296a957f6fSShawn McCarney         const json element = R"(
6306a957f6fSShawn McCarney             {
6316a957f6fSShawn McCarney                 "name": "VCS_CPU1",
6326a957f6fSShawn McCarney                 "check_status_vout": "false"
6336a957f6fSShawn McCarney             }
6346a957f6fSShawn McCarney         )"_json;
6356a957f6fSShawn McCarney         parseRail(element);
6366a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
6376a957f6fSShawn McCarney     }
6386a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
6396a957f6fSShawn McCarney     {
6406a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
6416a957f6fSShawn McCarney     }
6426a957f6fSShawn McCarney 
6439ec0d43dSShawn McCarney     // Test where fails: compare_voltage_to_limit value is invalid
6446a957f6fSShawn McCarney     try
6456a957f6fSShawn McCarney     {
6466a957f6fSShawn McCarney         const json element = R"(
6476a957f6fSShawn McCarney             {
6486a957f6fSShawn McCarney                 "name": "VCS_CPU1",
6499ec0d43dSShawn McCarney                 "compare_voltage_to_limit": 23
6506a957f6fSShawn McCarney             }
6516a957f6fSShawn McCarney         )"_json;
6526a957f6fSShawn McCarney         parseRail(element);
6536a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
6546a957f6fSShawn McCarney     }
6556a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
6566a957f6fSShawn McCarney     {
6576a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a boolean");
6586a957f6fSShawn McCarney     }
6596a957f6fSShawn McCarney 
6606a957f6fSShawn McCarney     // Test where fails: gpio value is invalid
6616a957f6fSShawn McCarney     try
6626a957f6fSShawn McCarney     {
6636a957f6fSShawn McCarney         const json element = R"(
6646a957f6fSShawn McCarney             {
6656a957f6fSShawn McCarney                 "name": "VCS_CPU1",
6666a957f6fSShawn McCarney                 "gpio": 131
6676a957f6fSShawn McCarney             }
6686a957f6fSShawn McCarney         )"_json;
6696a957f6fSShawn McCarney         parseRail(element);
6706a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
6716a957f6fSShawn McCarney     }
6726a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
6736a957f6fSShawn McCarney     {
6746a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
6756a957f6fSShawn McCarney     }
6766a957f6fSShawn McCarney 
6776a957f6fSShawn McCarney     // Test where fails: check_status_vout is true and page not specified
6786a957f6fSShawn McCarney     try
6796a957f6fSShawn McCarney     {
6806a957f6fSShawn McCarney         const json element = R"(
6816a957f6fSShawn McCarney             {
6826a957f6fSShawn McCarney                 "name": "VCS_CPU1",
6836a957f6fSShawn McCarney                 "check_status_vout": true
6846a957f6fSShawn McCarney             }
6856a957f6fSShawn McCarney         )"_json;
6866a957f6fSShawn McCarney         parseRail(element);
6876a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
6886a957f6fSShawn McCarney     }
6896a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
6906a957f6fSShawn McCarney     {
6916a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: page");
6926a957f6fSShawn McCarney     }
6936a957f6fSShawn McCarney 
6949ec0d43dSShawn McCarney     // Test where fails: compare_voltage_to_limit is true and page not
6956a957f6fSShawn McCarney     // specified
6966a957f6fSShawn McCarney     try
6976a957f6fSShawn McCarney     {
6986a957f6fSShawn McCarney         const json element = R"(
6996a957f6fSShawn McCarney             {
7006a957f6fSShawn McCarney                 "name": "VCS_CPU1",
7019ec0d43dSShawn McCarney                 "compare_voltage_to_limit": true
7026a957f6fSShawn McCarney             }
7036a957f6fSShawn McCarney         )"_json;
7046a957f6fSShawn McCarney         parseRail(element);
7056a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
7066a957f6fSShawn McCarney     }
7076a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
7086a957f6fSShawn McCarney     {
7096a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: page");
7106a957f6fSShawn McCarney     }
7116a957f6fSShawn McCarney 
7126a957f6fSShawn McCarney     // Test where fails: Invalid property specified
7136a957f6fSShawn McCarney     try
7146a957f6fSShawn McCarney     {
7156a957f6fSShawn McCarney         const json element = R"(
7166a957f6fSShawn McCarney             {
7176a957f6fSShawn McCarney                 "name": "VCS_CPU1",
7186a957f6fSShawn McCarney                 "foo": "bar"
7196a957f6fSShawn McCarney             }
7206a957f6fSShawn McCarney         )"_json;
7216a957f6fSShawn McCarney         parseRail(element);
7226a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
7236a957f6fSShawn McCarney     }
7246a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
7256a957f6fSShawn McCarney     {
7266a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
7276a957f6fSShawn McCarney     }
7286a957f6fSShawn McCarney }
7296a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseRailArray)7306a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseRailArray)
7316a957f6fSShawn McCarney {
7326a957f6fSShawn McCarney     // Test where works: Array is empty
7336a957f6fSShawn McCarney     {
7346a957f6fSShawn McCarney         const json element = R"(
7356a957f6fSShawn McCarney             [
7366a957f6fSShawn McCarney             ]
7376a957f6fSShawn McCarney         )"_json;
7386a957f6fSShawn McCarney         std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
7396a957f6fSShawn McCarney         EXPECT_EQ(rails.size(), 0);
7406a957f6fSShawn McCarney     }
7416a957f6fSShawn McCarney 
7426a957f6fSShawn McCarney     // Test where works: Array is not empty
7436a957f6fSShawn McCarney     {
7446a957f6fSShawn McCarney         const json element = R"(
7456a957f6fSShawn McCarney             [
7466a957f6fSShawn McCarney                 { "name": "VDD_CPU0" },
7476a957f6fSShawn McCarney                 { "name": "VCS_CPU1" }
7486a957f6fSShawn McCarney             ]
7496a957f6fSShawn McCarney         )"_json;
7506a957f6fSShawn McCarney         std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
7516a957f6fSShawn McCarney         EXPECT_EQ(rails.size(), 2);
7526a957f6fSShawn McCarney         EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
7536a957f6fSShawn McCarney         EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
7546a957f6fSShawn McCarney     }
7556a957f6fSShawn McCarney 
7566a957f6fSShawn McCarney     // Test where fails: Element is not an array
7576a957f6fSShawn McCarney     try
7586a957f6fSShawn McCarney     {
7596a957f6fSShawn McCarney         const json element = R"(
7606a957f6fSShawn McCarney             {
7616a957f6fSShawn McCarney                 "foo": "bar"
7626a957f6fSShawn McCarney             }
7636a957f6fSShawn McCarney         )"_json;
7646a957f6fSShawn McCarney         parseRailArray(element);
7656a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
7666a957f6fSShawn McCarney     }
7676a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
7686a957f6fSShawn McCarney     {
7696a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
7706a957f6fSShawn McCarney     }
7716a957f6fSShawn McCarney 
7726a957f6fSShawn McCarney     // Test where fails: Element within array is invalid
7736a957f6fSShawn McCarney     try
7746a957f6fSShawn McCarney     {
7756a957f6fSShawn McCarney         const json element = R"(
7766a957f6fSShawn McCarney             [
7776a957f6fSShawn McCarney                 { "name": "VDD_CPU0" },
7786a957f6fSShawn McCarney                 23
7796a957f6fSShawn McCarney             ]
7806a957f6fSShawn McCarney         )"_json;
7816a957f6fSShawn McCarney         parseRailArray(element);
7826a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
7836a957f6fSShawn McCarney     }
7846a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
7856a957f6fSShawn McCarney     {
7866a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
7876a957f6fSShawn McCarney     }
7886a957f6fSShawn McCarney }
7896a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseRoot)7906a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseRoot)
7916a957f6fSShawn McCarney {
7926a957f6fSShawn McCarney     // Test where works
7936a957f6fSShawn McCarney     {
7946a957f6fSShawn McCarney         const json element = R"(
7956a957f6fSShawn McCarney             {
7966a957f6fSShawn McCarney                 "rails": [
7976a957f6fSShawn McCarney                     {
7986a957f6fSShawn McCarney                         "name": "VDD_CPU0",
7996a957f6fSShawn McCarney                         "page": 11,
8006a957f6fSShawn McCarney                         "check_status_vout": true
8016a957f6fSShawn McCarney                     },
8026a957f6fSShawn McCarney                     {
8036a957f6fSShawn McCarney                         "name": "VCS_CPU1",
8046a957f6fSShawn McCarney                         "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
8056a957f6fSShawn McCarney                         "gpio": { "line": 60 }
8066a957f6fSShawn McCarney                     }
8076a957f6fSShawn McCarney                 ]
8086a957f6fSShawn McCarney             }
8096a957f6fSShawn McCarney         )"_json;
8106a957f6fSShawn McCarney         std::vector<std::unique_ptr<Rail>> rails = parseRoot(element);
8116a957f6fSShawn McCarney         EXPECT_EQ(rails.size(), 2);
8126a957f6fSShawn McCarney         EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
8136a957f6fSShawn McCarney         EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
8146a957f6fSShawn McCarney     }
8156a957f6fSShawn McCarney 
8166a957f6fSShawn McCarney     // Test where fails: Element is not an object
8176a957f6fSShawn McCarney     try
8186a957f6fSShawn McCarney     {
8196a957f6fSShawn McCarney         const json element = R"( [ "VDD_CPU0", "VCS_CPU1" ] )"_json;
8206a957f6fSShawn McCarney         parseRoot(element);
8216a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
8226a957f6fSShawn McCarney     }
8236a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
8246a957f6fSShawn McCarney     {
8256a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
8266a957f6fSShawn McCarney     }
8276a957f6fSShawn McCarney 
8286a957f6fSShawn McCarney     // Test where fails: Required rails property not specified
8296a957f6fSShawn McCarney     try
8306a957f6fSShawn McCarney     {
8316a957f6fSShawn McCarney         const json element = R"(
8326a957f6fSShawn McCarney             {
8336a957f6fSShawn McCarney             }
8346a957f6fSShawn McCarney         )"_json;
8356a957f6fSShawn McCarney         parseRoot(element);
8366a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
8376a957f6fSShawn McCarney     }
8386a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
8396a957f6fSShawn McCarney     {
8406a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Required property missing: rails");
8416a957f6fSShawn McCarney     }
8426a957f6fSShawn McCarney 
8436a957f6fSShawn McCarney     // Test where fails: rails value is invalid
8446a957f6fSShawn McCarney     try
8456a957f6fSShawn McCarney     {
8466a957f6fSShawn McCarney         const json element = R"(
8476a957f6fSShawn McCarney             {
8486a957f6fSShawn McCarney                 "rails": 31
8496a957f6fSShawn McCarney             }
8506a957f6fSShawn McCarney         )"_json;
8516a957f6fSShawn McCarney         parseRoot(element);
8526a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
8536a957f6fSShawn McCarney     }
8546a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
8556a957f6fSShawn McCarney     {
8566a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
8576a957f6fSShawn McCarney     }
8586a957f6fSShawn McCarney 
8596a957f6fSShawn McCarney     // Test where fails: Invalid property specified
8606a957f6fSShawn McCarney     try
8616a957f6fSShawn McCarney     {
8626a957f6fSShawn McCarney         const json element = R"(
8636a957f6fSShawn McCarney             {
8646a957f6fSShawn McCarney                 "rails": [
8656a957f6fSShawn McCarney                     {
8666a957f6fSShawn McCarney                         "name": "VDD_CPU0",
8676a957f6fSShawn McCarney                         "page": 11,
8686a957f6fSShawn McCarney                         "check_status_vout": true
8696a957f6fSShawn McCarney                     }
8706a957f6fSShawn McCarney                 ],
8716a957f6fSShawn McCarney                 "foo": true
8726a957f6fSShawn McCarney             }
8736a957f6fSShawn McCarney         )"_json;
8746a957f6fSShawn McCarney         parseRoot(element);
8756a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
8766a957f6fSShawn McCarney     }
8776a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
8786a957f6fSShawn McCarney     {
8796a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
8806a957f6fSShawn McCarney     }
8816a957f6fSShawn McCarney }
8826a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseString)8836a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseString)
8846a957f6fSShawn McCarney {
8856a957f6fSShawn McCarney     // Test where works: Empty string
8866a957f6fSShawn McCarney     {
8876a957f6fSShawn McCarney         const json element = "";
8886a957f6fSShawn McCarney         std::string value = parseString(element, true);
8896a957f6fSShawn McCarney         EXPECT_EQ(value, "");
8906a957f6fSShawn McCarney     }
8916a957f6fSShawn McCarney 
8926a957f6fSShawn McCarney     // Test where works: Non-empty string
8936a957f6fSShawn McCarney     {
8946a957f6fSShawn McCarney         const json element = "vdd_cpu1";
8956a957f6fSShawn McCarney         std::string value = parseString(element, false);
8966a957f6fSShawn McCarney         EXPECT_EQ(value, "vdd_cpu1");
8976a957f6fSShawn McCarney     }
8986a957f6fSShawn McCarney 
8996a957f6fSShawn McCarney     // Test where fails: Element is not a string
9006a957f6fSShawn McCarney     try
9016a957f6fSShawn McCarney     {
9026a957f6fSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
9036a957f6fSShawn McCarney         parseString(element);
9046a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9056a957f6fSShawn McCarney     }
9066a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9076a957f6fSShawn McCarney     {
9086a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not a string");
9096a957f6fSShawn McCarney     }
9106a957f6fSShawn McCarney 
9116a957f6fSShawn McCarney     // Test where fails: Empty string
9126a957f6fSShawn McCarney     try
9136a957f6fSShawn McCarney     {
9146a957f6fSShawn McCarney         const json element = "";
9156a957f6fSShawn McCarney         parseString(element);
9166a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9176a957f6fSShawn McCarney     }
9186a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9196a957f6fSShawn McCarney     {
9206a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an empty string");
9216a957f6fSShawn McCarney     }
9226a957f6fSShawn McCarney }
9236a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseUint8)9246a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseUint8)
9256a957f6fSShawn McCarney {
9266a957f6fSShawn McCarney     // Test where works: 0
9276a957f6fSShawn McCarney     {
9286a957f6fSShawn McCarney         const json element = R"( 0 )"_json;
9296a957f6fSShawn McCarney         uint8_t value = parseUint8(element);
9306a957f6fSShawn McCarney         EXPECT_EQ(value, 0);
9316a957f6fSShawn McCarney     }
9326a957f6fSShawn McCarney 
9336a957f6fSShawn McCarney     // Test where works: UINT8_MAX
9346a957f6fSShawn McCarney     {
9356a957f6fSShawn McCarney         const json element = R"( 255 )"_json;
9366a957f6fSShawn McCarney         uint8_t value = parseUint8(element);
9376a957f6fSShawn McCarney         EXPECT_EQ(value, 255);
9386a957f6fSShawn McCarney     }
9396a957f6fSShawn McCarney 
9406a957f6fSShawn McCarney     // Test where fails: Element is not an integer
9416a957f6fSShawn McCarney     try
9426a957f6fSShawn McCarney     {
9436a957f6fSShawn McCarney         const json element = R"( 1.03 )"_json;
9446a957f6fSShawn McCarney         parseUint8(element);
9456a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9466a957f6fSShawn McCarney     }
9476a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9486a957f6fSShawn McCarney     {
9496a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an integer");
9506a957f6fSShawn McCarney     }
9516a957f6fSShawn McCarney 
9526a957f6fSShawn McCarney     // Test where fails: Value < 0
9536a957f6fSShawn McCarney     try
9546a957f6fSShawn McCarney     {
9556a957f6fSShawn McCarney         const json element = R"( -1 )"_json;
9566a957f6fSShawn McCarney         parseUint8(element);
9576a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9586a957f6fSShawn McCarney     }
9596a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9606a957f6fSShawn McCarney     {
9616a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
9626a957f6fSShawn McCarney     }
9636a957f6fSShawn McCarney 
9646a957f6fSShawn McCarney     // Test where fails: Value > UINT8_MAX
9656a957f6fSShawn McCarney     try
9666a957f6fSShawn McCarney     {
9676a957f6fSShawn McCarney         const json element = R"( 256 )"_json;
9686a957f6fSShawn McCarney         parseUint8(element);
9696a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9706a957f6fSShawn McCarney     }
9716a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9726a957f6fSShawn McCarney     {
9736a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
9746a957f6fSShawn McCarney     }
9756a957f6fSShawn McCarney }
9766a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,ParseUnsignedInteger)9776a957f6fSShawn McCarney TEST(ConfigFileParserTests, ParseUnsignedInteger)
9786a957f6fSShawn McCarney {
9796a957f6fSShawn McCarney     // Test where works: 1
9806a957f6fSShawn McCarney     {
9816a957f6fSShawn McCarney         const json element = R"( 1 )"_json;
9826a957f6fSShawn McCarney         unsigned int value = parseUnsignedInteger(element);
9836a957f6fSShawn McCarney         EXPECT_EQ(value, 1);
9846a957f6fSShawn McCarney     }
9856a957f6fSShawn McCarney 
9866a957f6fSShawn McCarney     // Test where fails: Element is not an integer
9876a957f6fSShawn McCarney     try
9886a957f6fSShawn McCarney     {
9896a957f6fSShawn McCarney         const json element = R"( 1.5 )"_json;
9906a957f6fSShawn McCarney         parseUnsignedInteger(element);
9916a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
9926a957f6fSShawn McCarney     }
9936a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
9946a957f6fSShawn McCarney     {
9956a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
9966a957f6fSShawn McCarney     }
9976a957f6fSShawn McCarney 
9986a957f6fSShawn McCarney     // Test where fails: Value < 0
9996a957f6fSShawn McCarney     try
10006a957f6fSShawn McCarney     {
10016a957f6fSShawn McCarney         const json element = R"( -1 )"_json;
10026a957f6fSShawn McCarney         parseUnsignedInteger(element);
10036a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
10046a957f6fSShawn McCarney     }
10056a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
10066a957f6fSShawn McCarney     {
10076a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
10086a957f6fSShawn McCarney     }
10096a957f6fSShawn McCarney }
10106a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,VerifyIsArray)10116a957f6fSShawn McCarney TEST(ConfigFileParserTests, VerifyIsArray)
10126a957f6fSShawn McCarney {
10136a957f6fSShawn McCarney     // Test where element is an array
10146a957f6fSShawn McCarney     {
10156a957f6fSShawn McCarney         const json element = R"( [ "foo", "bar" ] )"_json;
10166a957f6fSShawn McCarney         verifyIsArray(element);
10176a957f6fSShawn McCarney     }
10186a957f6fSShawn McCarney 
10196a957f6fSShawn McCarney     // Test where element is not an array
10206a957f6fSShawn McCarney     try
10216a957f6fSShawn McCarney     {
10226a957f6fSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
10236a957f6fSShawn McCarney         verifyIsArray(element);
10246a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
10256a957f6fSShawn McCarney     }
10266a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
10276a957f6fSShawn McCarney     {
10286a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an array");
10296a957f6fSShawn McCarney     }
10306a957f6fSShawn McCarney }
10316a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,VerifyIsObject)10326a957f6fSShawn McCarney TEST(ConfigFileParserTests, VerifyIsObject)
10336a957f6fSShawn McCarney {
10346a957f6fSShawn McCarney     // Test where element is an object
10356a957f6fSShawn McCarney     {
10366a957f6fSShawn McCarney         const json element = R"( { "foo": "bar" } )"_json;
10376a957f6fSShawn McCarney         verifyIsObject(element);
10386a957f6fSShawn McCarney     }
10396a957f6fSShawn McCarney 
10406a957f6fSShawn McCarney     // Test where element is not an object
10416a957f6fSShawn McCarney     try
10426a957f6fSShawn McCarney     {
10436a957f6fSShawn McCarney         const json element = R"( [ "foo", "bar" ] )"_json;
10446a957f6fSShawn McCarney         verifyIsObject(element);
10456a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
10466a957f6fSShawn McCarney     }
10476a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
10486a957f6fSShawn McCarney     {
10496a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element is not an object");
10506a957f6fSShawn McCarney     }
10516a957f6fSShawn McCarney }
10526a957f6fSShawn McCarney 
TEST(ConfigFileParserTests,VerifyPropertyCount)10536a957f6fSShawn McCarney TEST(ConfigFileParserTests, VerifyPropertyCount)
10546a957f6fSShawn McCarney {
10556a957f6fSShawn McCarney     // Test where element has expected number of properties
10566a957f6fSShawn McCarney     {
10576a957f6fSShawn McCarney         const json element = R"(
10586a957f6fSShawn McCarney             {
10596a957f6fSShawn McCarney                 "line": 131,
10606a957f6fSShawn McCarney                 "active_low": true
10616a957f6fSShawn McCarney             }
10626a957f6fSShawn McCarney         )"_json;
10636a957f6fSShawn McCarney         verifyPropertyCount(element, 2);
10646a957f6fSShawn McCarney     }
10656a957f6fSShawn McCarney 
10666a957f6fSShawn McCarney     // Test where element has unexpected number of properties
10676a957f6fSShawn McCarney     try
10686a957f6fSShawn McCarney     {
10696a957f6fSShawn McCarney         const json element = R"(
10706a957f6fSShawn McCarney             {
10716a957f6fSShawn McCarney                 "line": 131,
10726a957f6fSShawn McCarney                 "active_low": true,
10736a957f6fSShawn McCarney                 "foo": 1.3
10746a957f6fSShawn McCarney             }
10756a957f6fSShawn McCarney         )"_json;
10766a957f6fSShawn McCarney         verifyPropertyCount(element, 2);
10776a957f6fSShawn McCarney         ADD_FAILURE() << "Should not have reached this line.";
10786a957f6fSShawn McCarney     }
10796a957f6fSShawn McCarney     catch (const std::invalid_argument& e)
10806a957f6fSShawn McCarney     {
10816a957f6fSShawn McCarney         EXPECT_STREQ(e.what(), "Element contains an invalid property");
10826a957f6fSShawn McCarney     }
10836a957f6fSShawn McCarney }
1084