xref: /openbmc/phosphor-power/test/json_parser_utils_tests.cpp (revision 38f8500414fe5c1be6f5159c563937289fe557c2)
1 /**
2  * Copyright © 2025 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "json_parser_utils.hpp"
17 
18 #include <nlohmann/json.hpp>
19 
20 #include <cstdint>
21 #include <exception>
22 #include <stdexcept>
23 #include <string>
24 #include <vector>
25 
26 #include <gtest/gtest.h>
27 
28 using namespace phosphor::power::json_parser_utils;
29 using json = nlohmann::json;
30 
31 TEST(JSONParserUtilsTests, GetRequiredProperty)
32 {
33     // Test where property exists
34     {
35         const json element = R"( { "format": "linear" } )"_json;
36         const json& propertyElement = getRequiredProperty(element, "format");
37         EXPECT_EQ(propertyElement.get<std::string>(), "linear");
38     }
39 
40     // Test where property does not exist
41     try
42     {
43         const json element = R"( { "volts": 1.03 } )"_json;
44         getRequiredProperty(element, "format");
45         ADD_FAILURE() << "Should not have reached this line.";
46     }
47     catch (const std::invalid_argument& e)
48     {
49         EXPECT_STREQ(e.what(), "Required property missing: format");
50     }
51 }
52 
53 TEST(JSONParserUtilsTests, ParseBitPosition)
54 {
55     // Test where works: 0
56     {
57         const json element = R"( 0 )"_json;
58         uint8_t value = parseBitPosition(element);
59         EXPECT_EQ(value, 0);
60     }
61 
62     // Test where works: 7
63     {
64         const json element = R"( 7 )"_json;
65         uint8_t value = parseBitPosition(element);
66         EXPECT_EQ(value, 7);
67     }
68 
69     // Test where fails: Element is not an integer
70     try
71     {
72         const json element = R"( 1.03 )"_json;
73         parseBitPosition(element);
74         ADD_FAILURE() << "Should not have reached this line.";
75     }
76     catch (const std::invalid_argument& e)
77     {
78         EXPECT_STREQ(e.what(), "Element is not an integer");
79     }
80 
81     // Test where fails: Value < 0
82     try
83     {
84         const json element = R"( -1 )"_json;
85         parseBitPosition(element);
86         ADD_FAILURE() << "Should not have reached this line.";
87     }
88     catch (const std::invalid_argument& e)
89     {
90         EXPECT_STREQ(e.what(), "Element is not a bit position");
91     }
92 
93     // Test where fails: Value > 7
94     try
95     {
96         const json element = R"( 8 )"_json;
97         parseBitPosition(element);
98         ADD_FAILURE() << "Should not have reached this line.";
99     }
100     catch (const std::invalid_argument& e)
101     {
102         EXPECT_STREQ(e.what(), "Element is not a bit position");
103     }
104 }
105 
106 TEST(JSONParserUtilsTests, ParseBitValue)
107 {
108     // Test where works: 0
109     {
110         const json element = R"( 0 )"_json;
111         uint8_t value = parseBitValue(element);
112         EXPECT_EQ(value, 0);
113     }
114 
115     // Test where works: 1
116     {
117         const json element = R"( 1 )"_json;
118         uint8_t value = parseBitValue(element);
119         EXPECT_EQ(value, 1);
120     }
121 
122     // Test where fails: Element is not an integer
123     try
124     {
125         const json element = R"( 0.5 )"_json;
126         parseBitValue(element);
127         ADD_FAILURE() << "Should not have reached this line.";
128     }
129     catch (const std::invalid_argument& e)
130     {
131         EXPECT_STREQ(e.what(), "Element is not an integer");
132     }
133 
134     // Test where fails: Value < 0
135     try
136     {
137         const json element = R"( -1 )"_json;
138         parseBitValue(element);
139         ADD_FAILURE() << "Should not have reached this line.";
140     }
141     catch (const std::invalid_argument& e)
142     {
143         EXPECT_STREQ(e.what(), "Element is not a bit value");
144     }
145 
146     // Test where fails: Value > 1
147     try
148     {
149         const json element = R"( 2 )"_json;
150         parseBitValue(element);
151         ADD_FAILURE() << "Should not have reached this line.";
152     }
153     catch (const std::invalid_argument& e)
154     {
155         EXPECT_STREQ(e.what(), "Element is not a bit value");
156     }
157 }
158 
159 TEST(JSONParserUtilsTests, ParseBoolean)
160 {
161     // Test where works: true
162     {
163         const json element = R"( true )"_json;
164         bool value = parseBoolean(element);
165         EXPECT_EQ(value, true);
166     }
167 
168     // Test where works: false
169     {
170         const json element = R"( false )"_json;
171         bool value = parseBoolean(element);
172         EXPECT_EQ(value, false);
173     }
174 
175     // Test where fails: Element is not a boolean
176     try
177     {
178         const json element = R"( 1 )"_json;
179         parseBoolean(element);
180         ADD_FAILURE() << "Should not have reached this line.";
181     }
182     catch (const std::invalid_argument& e)
183     {
184         EXPECT_STREQ(e.what(), "Element is not a boolean");
185     }
186 }
187 
188 TEST(JSONParserUtilsTests, ParseDouble)
189 {
190     // Test where works: floating point value
191     {
192         const json element = R"( 1.03 )"_json;
193         double value = parseDouble(element);
194         EXPECT_EQ(value, 1.03);
195     }
196 
197     // Test where works: integer value
198     {
199         const json element = R"( 24 )"_json;
200         double value = parseDouble(element);
201         EXPECT_EQ(value, 24.0);
202     }
203 
204     // Test where fails: Element is not a number
205     try
206     {
207         const json element = R"( true )"_json;
208         parseDouble(element);
209         ADD_FAILURE() << "Should not have reached this line.";
210     }
211     catch (const std::invalid_argument& e)
212     {
213         EXPECT_STREQ(e.what(), "Element is not a number");
214     }
215 }
216 
217 TEST(JSONParserUtilsTests, ParseHexByte)
218 {
219     // Test where works: "0xFF"
220     {
221         const json element = R"( "0xFF" )"_json;
222         uint8_t value = parseHexByte(element);
223         EXPECT_EQ(value, 0xFF);
224     }
225 
226     // Test where works: "0xff"
227     {
228         const json element = R"( "0xff" )"_json;
229         uint8_t value = parseHexByte(element);
230         EXPECT_EQ(value, 0xff);
231     }
232 
233     // Test where works: "0xf"
234     {
235         const json element = R"( "0xf" )"_json;
236         uint8_t value = parseHexByte(element);
237         EXPECT_EQ(value, 0xf);
238     }
239 
240     // Test where fails: "0xfff"
241     try
242     {
243         const json element = R"( "0xfff" )"_json;
244         parseHexByte(element);
245         ADD_FAILURE() << "Should not have reached this line.";
246     }
247     catch (const std::invalid_argument& e)
248     {
249         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
250     }
251 
252     // Test where fails: "0xAG"
253     try
254     {
255         const json element = R"( "0xAG" )"_json;
256         parseHexByte(element);
257         ADD_FAILURE() << "Should not have reached this line.";
258     }
259     catch (const std::invalid_argument& e)
260     {
261         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
262     }
263 
264     // Test where fails: "ff"
265     try
266     {
267         const json element = R"( "ff" )"_json;
268         parseHexByte(element);
269         ADD_FAILURE() << "Should not have reached this line.";
270     }
271     catch (const std::invalid_argument& e)
272     {
273         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
274     }
275 
276     // Test where fails: ""
277     try
278     {
279         const json element = "";
280         parseHexByte(element);
281         ADD_FAILURE() << "Should not have reached this line.";
282     }
283     catch (const std::invalid_argument& e)
284     {
285         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
286     }
287 
288     // Test where fails: "f"
289     try
290     {
291         const json element = R"( "f" )"_json;
292         parseHexByte(element);
293         ADD_FAILURE() << "Should not have reached this line.";
294     }
295     catch (const std::invalid_argument& e)
296     {
297         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
298     }
299 
300     // Test where fails: "0x"
301     try
302     {
303         const json element = R"( "0x" )"_json;
304         parseHexByte(element);
305         ADD_FAILURE() << "Should not have reached this line.";
306     }
307     catch (const std::invalid_argument& e)
308     {
309         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
310     }
311 
312     // Test where fails: "0Xff"
313     try
314     {
315         const json element = R"( "0XFF" )"_json;
316         parseHexByte(element);
317         ADD_FAILURE() << "Should not have reached this line.";
318     }
319     catch (const std::invalid_argument& e)
320     {
321         EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
322     }
323 }
324 
325 TEST(JSONParserUtilsTests, ParseHexByteArray)
326 {
327     // Test where works
328     {
329         const json element = R"( [ "0xCC", "0xFF" ] )"_json;
330         std::vector<uint8_t> hexBytes = parseHexByteArray(element);
331         std::vector<uint8_t> expected = {0xcc, 0xff};
332         EXPECT_EQ(hexBytes, expected);
333     }
334 
335     // Test where fails: Element is not an array
336     try
337     {
338         const json element = 0;
339         parseHexByteArray(element);
340         ADD_FAILURE() << "Should not have reached this line.";
341     }
342     catch (const std::invalid_argument& e)
343     {
344         EXPECT_STREQ(e.what(), "Element is not an array");
345     }
346 }
347 
348 TEST(JSONParserUtilsTests, ParseInt8)
349 {
350     // Test where works: INT8_MIN
351     {
352         const json element = R"( -128 )"_json;
353         int8_t value = parseInt8(element);
354         EXPECT_EQ(value, -128);
355     }
356 
357     // Test where works: INT8_MAX
358     {
359         const json element = R"( 127 )"_json;
360         int8_t value = parseInt8(element);
361         EXPECT_EQ(value, 127);
362     }
363 
364     // Test where fails: Element is not an integer
365     try
366     {
367         const json element = R"( 1.03 )"_json;
368         parseInt8(element);
369         ADD_FAILURE() << "Should not have reached this line.";
370     }
371     catch (const std::invalid_argument& e)
372     {
373         EXPECT_STREQ(e.what(), "Element is not an integer");
374     }
375 
376     // Test where fails: Value < INT8_MIN
377     try
378     {
379         const json element = R"( -129 )"_json;
380         parseInt8(element);
381         ADD_FAILURE() << "Should not have reached this line.";
382     }
383     catch (const std::invalid_argument& e)
384     {
385         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
386     }
387 
388     // Test where fails: Value > INT8_MAX
389     try
390     {
391         const json element = R"( 128 )"_json;
392         parseInt8(element);
393         ADD_FAILURE() << "Should not have reached this line.";
394     }
395     catch (const std::invalid_argument& e)
396     {
397         EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
398     }
399 }
400 
401 TEST(JSONParserUtilsTests, ParseString)
402 {
403     // Test where works: Empty string
404     {
405         const json element = "";
406         std::string value = parseString(element, true);
407         EXPECT_EQ(value, "");
408     }
409 
410     // Test where works: Non-empty string
411     {
412         const json element = "vdd_regulator";
413         std::string value = parseString(element, false);
414         EXPECT_EQ(value, "vdd_regulator");
415     }
416 
417     // Test where fails: Element is not a string
418     try
419     {
420         const json element = R"( { "foo": "bar" } )"_json;
421         parseString(element);
422         ADD_FAILURE() << "Should not have reached this line.";
423     }
424     catch (const std::invalid_argument& e)
425     {
426         EXPECT_STREQ(e.what(), "Element is not a string");
427     }
428 
429     // Test where fails: Empty string
430     try
431     {
432         const json element = "";
433         parseString(element);
434         ADD_FAILURE() << "Should not have reached this line.";
435     }
436     catch (const std::invalid_argument& e)
437     {
438         EXPECT_STREQ(e.what(), "Element contains an empty string");
439     }
440 }
441 
442 TEST(JSONParserUtilsTests, ParseUint8)
443 {
444     // Test where works: 0
445     {
446         const json element = R"( 0 )"_json;
447         uint8_t value = parseUint8(element);
448         EXPECT_EQ(value, 0);
449     }
450 
451     // Test where works: UINT8_MAX
452     {
453         const json element = R"( 255 )"_json;
454         uint8_t value = parseUint8(element);
455         EXPECT_EQ(value, 255);
456     }
457 
458     // Test where fails: Element is not an integer
459     try
460     {
461         const json element = R"( 1.03 )"_json;
462         parseUint8(element);
463         ADD_FAILURE() << "Should not have reached this line.";
464     }
465     catch (const std::invalid_argument& e)
466     {
467         EXPECT_STREQ(e.what(), "Element is not an integer");
468     }
469 
470     // Test where fails: Value < 0
471     try
472     {
473         const json element = R"( -1 )"_json;
474         parseUint8(element);
475         ADD_FAILURE() << "Should not have reached this line.";
476     }
477     catch (const std::invalid_argument& e)
478     {
479         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
480     }
481 
482     // Test where fails: Value > UINT8_MAX
483     try
484     {
485         const json element = R"( 256 )"_json;
486         parseUint8(element);
487         ADD_FAILURE() << "Should not have reached this line.";
488     }
489     catch (const std::invalid_argument& e)
490     {
491         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
492     }
493 }
494 
495 TEST(JSONParserUtilsTests, ParseUnsignedInteger)
496 {
497     // Test where works: 1
498     {
499         const json element = R"( 1 )"_json;
500         unsigned int value = parseUnsignedInteger(element);
501         EXPECT_EQ(value, 1);
502     }
503 
504     // Test where fails: Element is not an integer
505     try
506     {
507         const json element = R"( 1.5 )"_json;
508         parseUnsignedInteger(element);
509         ADD_FAILURE() << "Should not have reached this line.";
510     }
511     catch (const std::invalid_argument& e)
512     {
513         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
514     }
515 
516     // Test where fails: Value < 0
517     try
518     {
519         const json element = R"( -1 )"_json;
520         parseUnsignedInteger(element);
521         ADD_FAILURE() << "Should not have reached this line.";
522     }
523     catch (const std::invalid_argument& e)
524     {
525         EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
526     }
527 }
528 
529 TEST(JSONParserUtilsTests, VerifyIsArray)
530 {
531     // Test where element is an array
532     {
533         const json element = R"( [ "foo", "bar" ] )"_json;
534         verifyIsArray(element);
535     }
536 
537     // Test where element is not an array
538     try
539     {
540         const json element = R"( { "foo": "bar" } )"_json;
541         verifyIsArray(element);
542         ADD_FAILURE() << "Should not have reached this line.";
543     }
544     catch (const std::invalid_argument& e)
545     {
546         EXPECT_STREQ(e.what(), "Element is not an array");
547     }
548 }
549 
550 TEST(JSONParserUtilsTests, VerifyIsObject)
551 {
552     // Test where element is an object
553     {
554         const json element = R"( { "foo": "bar" } )"_json;
555         verifyIsObject(element);
556     }
557 
558     // Test where element is not an object
559     try
560     {
561         const json element = R"( [ "foo", "bar" ] )"_json;
562         verifyIsObject(element);
563         ADD_FAILURE() << "Should not have reached this line.";
564     }
565     catch (const std::invalid_argument& e)
566     {
567         EXPECT_STREQ(e.what(), "Element is not an object");
568     }
569 }
570 
571 TEST(JSONParserUtilsTests, VerifyPropertyCount)
572 {
573     // Test where element has expected number of properties
574     {
575         const json element = R"(
576             {
577               "comments": [ "Set voltage rule" ],
578               "id": "set_voltage_rule"
579             }
580         )"_json;
581         verifyPropertyCount(element, 2);
582     }
583 
584     // Test where element has unexpected number of properties
585     try
586     {
587         const json element = R"(
588             {
589               "comments": [ "Set voltage rule" ],
590               "id": "set_voltage_rule",
591               "foo": 1.3
592             }
593         )"_json;
594         verifyPropertyCount(element, 2);
595         ADD_FAILURE() << "Should not have reached this line.";
596     }
597     catch (const std::invalid_argument& e)
598     {
599         EXPECT_STREQ(e.what(), "Element contains an invalid property");
600     }
601 }
602