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