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