blob: c26f4720aadab17456256a1c46ca9dae0dea8459 [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"
Bob Kingf617f892020-03-30 19:03:35 +080020#include "i2c_interface.hpp"
21#include "i2c_write_bit_action.hpp"
Shawn McCarney0e8c68a2020-03-27 01:44:48 -050022#include "pmbus_utils.hpp"
23#include "pmbus_write_vout_command_action.hpp"
24#include "rule.hpp"
Shawn McCarney80c0b042020-03-27 12:08:53 -050025#include "tmp_file.hpp"
Shawn McCarney0e8c68a2020-03-27 01:44:48 -050026
Shawn McCarney80c0b042020-03-27 12:08:53 -050027#include <sys/stat.h> // for chmod()
Shawn McCarney0e8c68a2020-03-27 01:44:48 -050028
29#include <nlohmann/json.hpp>
30
31#include <cstdint>
32#include <cstring>
33#include <exception>
34#include <filesystem>
35#include <fstream>
36#include <memory>
37#include <optional>
38#include <stdexcept>
39#include <string>
40#include <tuple>
41#include <vector>
42
43#include <gtest/gtest.h>
44
45using namespace phosphor::power::regulators;
46using namespace phosphor::power::regulators::config_file_parser;
47using namespace phosphor::power::regulators::config_file_parser::internal;
48using json = nlohmann::json;
49
Shawn McCarney0e8c68a2020-03-27 01:44:48 -050050void writeConfigFile(const std::filesystem::path& pathName,
51 const std::string& contents)
52{
53 std::ofstream file{pathName};
54 file << contents;
55}
56
57void writeConfigFile(const std::filesystem::path& pathName,
58 const json& contents)
59{
60 std::ofstream file{pathName};
61 file << contents;
62}
63
64TEST(ConfigFileParserTests, Parse)
65{
66 // Test where works
67 {
68 const json configFileContents = R"(
69 {
70 "rules": [
71 {
72 "id": "set_voltage_rule1",
73 "actions": [
74 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
75 ]
76 },
77 {
78 "id": "set_voltage_rule2",
79 "actions": [
80 { "pmbus_write_vout_command": { "volts": 1.33, "format": "linear" } }
81 ]
82 }
83 ],
84 "chassis": [
85 { "number": 1 },
86 { "number": 2 },
87 { "number": 3 }
88 ]
89 }
90 )"_json;
91
92 TmpFile configFile;
93 std::filesystem::path pathName{configFile.getName()};
94 writeConfigFile(pathName, configFileContents);
95
96 std::vector<std::unique_ptr<Rule>> rules{};
97 std::vector<std::unique_ptr<Chassis>> chassis{};
98 std::tie(rules, chassis) = parse(pathName);
99
100 EXPECT_EQ(rules.size(), 2);
101 EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
102 EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
103
104 // TODO: Not implemented yet
105 // EXPECT_EQ(chassis.size(), 3);
106 // EXPECT_EQ(chassis[0]->getNumber(), 1);
107 // EXPECT_EQ(chassis[1]->getNumber(), 2);
108 // EXPECT_EQ(chassis[2]->getNumber(), 3);
109 }
110
111 // Test where fails: File does not exist
112 try
113 {
114 std::filesystem::path pathName{"/tmp/non_existent_file"};
115 parse(pathName);
116 ADD_FAILURE() << "Should not have reached this line.";
117 }
118 catch (const ConfigFileParserError& e)
119 {
120 // Expected exception; what() message will vary
121 }
122
123 // Test where fails: File is not readable
124 try
125 {
126 const json configFileContents = R"(
127 {
128 "chassis": [ { "number": 1 } ]
129 }
130 )"_json;
131
132 TmpFile configFile;
133 std::filesystem::path pathName{configFile.getName()};
134 writeConfigFile(pathName, configFileContents);
135
136 chmod(pathName.c_str(), 0222);
137
138 parse(pathName);
139 ADD_FAILURE() << "Should not have reached this line.";
140 }
141 catch (const ConfigFileParserError& e)
142 {
143 // Expected exception; what() message will vary
144 }
145
146 // Test where fails: File is not valid JSON
147 try
148 {
149 const std::string configFileContents = "] foo [";
150
151 TmpFile configFile;
152 std::filesystem::path pathName{configFile.getName()};
153 writeConfigFile(pathName, configFileContents);
154
155 parse(pathName);
156 ADD_FAILURE() << "Should not have reached this line.";
157 }
158 catch (const ConfigFileParserError& e)
159 {
160 // Expected exception; what() message will vary
161 }
162
163 // Test where fails: Error when parsing JSON elements
164 try
165 {
166 const json configFileContents = R"( { "foo": "bar" } )"_json;
167
168 TmpFile configFile;
169 std::filesystem::path pathName{configFile.getName()};
170 writeConfigFile(pathName, configFileContents);
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
181TEST(ConfigFileParserTests, GetRequiredProperty)
182{
183 // Test where property exists
184 {
185 const json element = R"( { "format": "linear" } )"_json;
186 const json& propertyElement = getRequiredProperty(element, "format");
187 EXPECT_EQ(propertyElement.get<std::string>(), "linear");
188 }
189
190 // Test where property does not exist
191 try
192 {
193 const json element = R"( { "volts": 1.03 } )"_json;
194 getRequiredProperty(element, "format");
195 ADD_FAILURE() << "Should not have reached this line.";
196 }
197 catch (const std::invalid_argument& e)
198 {
199 EXPECT_STREQ(e.what(), "Required property missing: format");
200 }
201}
202
203TEST(ConfigFileParserTests, ParseAction)
204{
205 // Test where works: comments property specified
206 {
207 const json element = R"(
208 {
209 "comments": [ "Set output voltage." ],
210 "pmbus_write_vout_command": {
211 "format": "linear"
212 }
213 }
214 )"_json;
215 std::unique_ptr<Action> action = parseAction(element);
216 EXPECT_NE(action.get(), nullptr);
217 }
218
219 // Test where works: comments property not specified
220 {
221 const json element = R"(
222 {
223 "pmbus_write_vout_command": {
224 "format": "linear"
225 }
226 }
227 )"_json;
228 std::unique_ptr<Action> action = parseAction(element);
229 EXPECT_NE(action.get(), nullptr);
230 }
231
232 // Test where works: and action type specified
233 // TODO: Not implemented yet
234
235 // Test where works: compare_presence action type specified
236 // TODO: Not implemented yet
237
238 // Test where works: compare_vpd action type specified
239 // TODO: Not implemented yet
240
241 // Test where works: i2c_compare_bit action type specified
242 // TODO: Not implemented yet
243
244 // Test where works: i2c_compare_byte action type specified
245 // TODO: Not implemented yet
246
247 // Test where works: i2c_compare_bytes action type specified
248 // TODO: Not implemented yet
249
250 // Test where works: i2c_write_bit action type specified
Bob Kingf617f892020-03-30 19:03:35 +0800251 {
252 const json element = R"(
253 {
254 "i2c_write_bit": {
255 "register": "0xA0",
256 "position": 3,
257 "value": 0
258 }
259 }
260 )"_json;
261 std::unique_ptr<Action> action = parseAction(element);
262 EXPECT_NE(action.get(), nullptr);
263 }
Shawn McCarney0e8c68a2020-03-27 01:44:48 -0500264
265 // Test where works: i2c_write_byte action type specified
266 // TODO: Not implemented yet
267
268 // Test where works: i2c_write_bytes action type specified
269 // TODO: Not implemented yet
270
271 // Test where works: if action type specified
272 // TODO: Not implemented yet
273
274 // Test where works: not action type specified
275 // TODO: Not implemented yet
276
277 // Test where works: or action type specified
278 // TODO: Not implemented yet
279
280 // Test where works: pmbus_read_sensor action type specified
281 // TODO: Not implemented yet
282
283 // Test where works: pmbus_write_vout_command action type specified
284 {
285 const json element = R"(
286 {
287 "pmbus_write_vout_command": {
288 "format": "linear"
289 }
290 }
291 )"_json;
292 std::unique_ptr<Action> action = parseAction(element);
293 EXPECT_NE(action.get(), nullptr);
294 }
295
296 // Test where works: run_rule action type specified
297 // TODO: Not implemented yet
298
299 // Test where works: set_device action type specified
300 // TODO: Not implemented yet
301
302 // Test where fails: Element is not an object
303 try
304 {
305 const json element = R"( [ "0xFF", "0x01" ] )"_json;
306 parseAction(element);
307 ADD_FAILURE() << "Should not have reached this line.";
308 }
309 catch (const std::invalid_argument& e)
310 {
311 EXPECT_STREQ(e.what(), "Element is not an object");
312 }
313
314 // Test where fails: No action type specified
315 try
316 {
317 const json element = R"(
318 {
319 "comments": [ "Set output voltage." ]
320 }
321 )"_json;
322 parseAction(element);
323 ADD_FAILURE() << "Should not have reached this line.";
324 }
325 catch (const std::invalid_argument& e)
326 {
327 EXPECT_STREQ(e.what(), "Required action type property missing");
328 }
329
330 // Test where fails: Multiple action types specified
331 // TODO: Implement after another action type is supported
332
333 // Test where fails: Invalid property specified
334 try
335 {
336 const json element = R"(
337 {
338 "remarks": [ "Set output voltage." ],
339 "pmbus_write_vout_command": {
340 "format": "linear"
341 }
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(), "Element contains an invalid property");
350 }
351}
352
353TEST(ConfigFileParserTests, ParseActionArray)
354{
355 // Test where works
356 {
357 const json element = R"(
358 [
359 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
360 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
361 ]
362 )"_json;
363 std::vector<std::unique_ptr<Action>> actions =
364 parseActionArray(element);
365 EXPECT_EQ(actions.size(), 2);
366 }
367
368 // Test where fails: Element is not an array
369 try
370 {
371 const json element = R"(
372 {
373 "foo": "bar"
374 }
375 )"_json;
376 parseActionArray(element);
377 ADD_FAILURE() << "Should not have reached this line.";
378 }
379 catch (const std::invalid_argument& e)
380 {
381 EXPECT_STREQ(e.what(), "Element is not an array");
382 }
383}
384
Bob Kingf617f892020-03-30 19:03:35 +0800385TEST(ConfigFileParserTests, ParseBitPosition)
386{
387 // Test where works: 0
388 {
389 const json element = R"( 0 )"_json;
390 uint8_t value = parseBitPosition(element);
391 EXPECT_EQ(value, 0);
392 }
393
394 // Test where works: 7
395 {
396 const json element = R"( 7 )"_json;
397 uint8_t value = parseBitPosition(element);
398 EXPECT_EQ(value, 7);
399 }
400
401 // Test where fails: Element is not an integer
402 try
403 {
404 const json element = R"( 1.03 )"_json;
405 parseBitPosition(element);
406 ADD_FAILURE() << "Should not have reached this line.";
407 }
408 catch (const std::invalid_argument& e)
409 {
410 EXPECT_STREQ(e.what(), "Element is not an integer");
411 }
412
413 // Test where fails: Value < 0
414 try
415 {
416 const json element = R"( -1 )"_json;
417 parseBitPosition(element);
418 ADD_FAILURE() << "Should not have reached this line.";
419 }
420 catch (const std::invalid_argument& e)
421 {
422 EXPECT_STREQ(e.what(), "Element is not a bit position");
423 }
424
425 // Test where fails: Value > 7
426 try
427 {
428 const json element = R"( 8 )"_json;
429 parseBitPosition(element);
430 ADD_FAILURE() << "Should not have reached this line.";
431 }
432 catch (const std::invalid_argument& e)
433 {
434 EXPECT_STREQ(e.what(), "Element is not a bit position");
435 }
436}
437
438TEST(ConfigFileParserTests, ParseBitValue)
439{
440 // Test where works: 0
441 {
442 const json element = R"( 0 )"_json;
443 uint8_t value = parseBitValue(element);
444 EXPECT_EQ(value, 0);
445 }
446
447 // Test where works: 1
448 {
449 const json element = R"( 1 )"_json;
450 uint8_t value = parseBitValue(element);
451 EXPECT_EQ(value, 1);
452 }
453
454 // Test where fails: Element is not an integer
455 try
456 {
457 const json element = R"( 0.5 )"_json;
458 parseBitValue(element);
459 ADD_FAILURE() << "Should not have reached this line.";
460 }
461 catch (const std::invalid_argument& e)
462 {
463 EXPECT_STREQ(e.what(), "Element is not an integer");
464 }
465
466 // Test where fails: Value < 0
467 try
468 {
469 const json element = R"( -1 )"_json;
470 parseBitValue(element);
471 ADD_FAILURE() << "Should not have reached this line.";
472 }
473 catch (const std::invalid_argument& e)
474 {
475 EXPECT_STREQ(e.what(), "Element is not a bit value");
476 }
477
478 // Test where fails: Value > 1
479 try
480 {
481 const json element = R"( 2 )"_json;
482 parseBitValue(element);
483 ADD_FAILURE() << "Should not have reached this line.";
484 }
485 catch (const std::invalid_argument& e)
486 {
487 EXPECT_STREQ(e.what(), "Element is not a bit value");
488 }
489}
490
Shawn McCarney0e8c68a2020-03-27 01:44:48 -0500491TEST(ConfigFileParserTests, ParseBoolean)
492{
493 // Test where works: true
494 {
495 const json element = R"( true )"_json;
496 bool value = parseBoolean(element);
497 EXPECT_EQ(value, true);
498 }
499
500 // Test where works: false
501 {
502 const json element = R"( false )"_json;
503 bool value = parseBoolean(element);
504 EXPECT_EQ(value, false);
505 }
506
507 // Test where fails: Element is not a boolean
508 try
509 {
510 const json element = R"( 1 )"_json;
511 parseBoolean(element);
512 ADD_FAILURE() << "Should not have reached this line.";
513 }
514 catch (const std::invalid_argument& e)
515 {
516 EXPECT_STREQ(e.what(), "Element is not a boolean");
517 }
518}
519
520TEST(ConfigFileParserTests, ParseChassisArray)
521{
522 // TODO: Not implemented yet
523}
524
525TEST(ConfigFileParserTests, ParseDouble)
526{
527 // Test where works: floating point value
528 {
529 const json element = R"( 1.03 )"_json;
530 double value = parseDouble(element);
531 EXPECT_EQ(value, 1.03);
532 }
533
534 // Test where works: integer value
535 {
536 const json element = R"( 24 )"_json;
537 double value = parseDouble(element);
538 EXPECT_EQ(value, 24.0);
539 }
540
541 // Test where fails: Element is not a number
542 try
543 {
544 const json element = R"( true )"_json;
545 parseDouble(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
554TEST(ConfigFileParserTests, ParseInt8)
555{
556 // Test where works: INT8_MIN
557 {
558 const json element = R"( -128 )"_json;
559 int8_t value = parseInt8(element);
560 EXPECT_EQ(value, -128);
561 }
562
563 // Test where works: INT8_MAX
564 {
565 const json element = R"( 127 )"_json;
566 int8_t value = parseInt8(element);
567 EXPECT_EQ(value, 127);
568 }
569
570 // Test where fails: Element is not an integer
571 try
572 {
573 const json element = R"( 1.03 )"_json;
574 parseInt8(element);
575 ADD_FAILURE() << "Should not have reached this line.";
576 }
577 catch (const std::invalid_argument& e)
578 {
579 EXPECT_STREQ(e.what(), "Element is not an integer");
580 }
581
582 // Test where fails: Value < INT8_MIN
583 try
584 {
585 const json element = R"( -129 )"_json;
586 parseInt8(element);
587 ADD_FAILURE() << "Should not have reached this line.";
588 }
589 catch (const std::invalid_argument& e)
590 {
591 EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
592 }
593
594 // Test where fails: Value > INT8_MAX
595 try
596 {
597 const json element = R"( 128 )"_json;
598 parseInt8(element);
599 ADD_FAILURE() << "Should not have reached this line.";
600 }
601 catch (const std::invalid_argument& e)
602 {
603 EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer");
604 }
605}
606
Bob Kingf617f892020-03-30 19:03:35 +0800607TEST(ConfigFileParserTests, ParseI2CWriteBit)
608{
609 // Test where works
610 {
611 const json element = R"(
612 {
613 "register": "0xA0",
614 "position": 3,
615 "value": 0
616 }
617 )"_json;
618 std::unique_ptr<I2CWriteBitAction> action = parseI2CWriteBit(element);
619 EXPECT_EQ(action->getRegister(), 0xA0);
620 EXPECT_EQ(action->getPosition(), 3);
621 EXPECT_EQ(action->getValue(), 0);
622 }
623
624 // Test where fails: Invalid property specified
625 try
626 {
627 const json element = R"(
628 {
629 "register": "0xA0",
630 "position": 3,
631 "value": 0,
632 "foo": 3
633 }
634 )"_json;
635 parseI2CWriteBit(element);
636 ADD_FAILURE() << "Should not have reached this line.";
637 }
638 catch (const std::invalid_argument& e)
639 {
640 EXPECT_STREQ(e.what(), "Element contains an invalid property");
641 }
642
643 // Test where fails: Element is not an object
644 try
645 {
646 const json element = R"( [ "0xFF", "0x01" ] )"_json;
647 parseI2CWriteBit(element);
648 ADD_FAILURE() << "Should not have reached this line.";
649 }
650 catch (const std::invalid_argument& e)
651 {
652 EXPECT_STREQ(e.what(), "Element is not an object");
653 }
654
655 // Test where fails: register value is invalid
656 try
657 {
658 const json element = R"(
659 {
660 "register": "0xAG",
661 "position": 3,
662 "value": 0
663 }
664 )"_json;
665 parseI2CWriteBit(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 is not hexadecimal string");
671 }
672
673 // Test where fails: position value is invalid
674 try
675 {
676 const json element = R"(
677 {
678 "register": "0xA0",
679 "position": 8,
680 "value": 0
681 }
682 )"_json;
683 parseI2CWriteBit(element);
684 ADD_FAILURE() << "Should not have reached this line.";
685 }
686 catch (const std::invalid_argument& e)
687 {
688 EXPECT_STREQ(e.what(), "Element is not a bit position");
689 }
690
691 // Test where fails: value value is invalid
692 try
693 {
694 const json element = R"(
695 {
696 "register": "0xA0",
697 "position": 3,
698 "value": 2
699 }
700 )"_json;
701 parseI2CWriteBit(element);
702 ADD_FAILURE() << "Should not have reached this line.";
703 }
704 catch (const std::invalid_argument& e)
705 {
706 EXPECT_STREQ(e.what(), "Element is not a bit value");
707 }
708
709 // Test where fails: Required register property not specified
710 try
711 {
712 const json element = R"(
713 {
714 "position": 3,
715 "value": 0
716 }
717 )"_json;
718 parseI2CWriteBit(element);
719 ADD_FAILURE() << "Should not have reached this line.";
720 }
721 catch (const std::invalid_argument& e)
722 {
723 EXPECT_STREQ(e.what(), "Required property missing: register");
724 }
725
726 // Test where fails: Required position property not specified
727 try
728 {
729 const json element = R"(
730 {
731 "register": "0xA0",
732 "value": 0
733 }
734 )"_json;
735 parseI2CWriteBit(element);
736 ADD_FAILURE() << "Should not have reached this line.";
737 }
738 catch (const std::invalid_argument& e)
739 {
740 EXPECT_STREQ(e.what(), "Required property missing: position");
741 }
742
743 // Test where fails: Required value property not specified
744 try
745 {
746 const json element = R"(
747 {
748 "register": "0xA0",
749 "position": 3
750 }
751 )"_json;
752 parseI2CWriteBit(element);
753 ADD_FAILURE() << "Should not have reached this line.";
754 }
755 catch (const std::invalid_argument& e)
756 {
757 EXPECT_STREQ(e.what(), "Required property missing: value");
758 }
759}
760
Shawn McCarney0e8c68a2020-03-27 01:44:48 -0500761TEST(ConfigFileParserTests, ParsePMBusWriteVoutCommand)
762{
763 // Test where works: Only required properties specified
764 {
765 const json element = R"(
766 {
767 "format": "linear"
768 }
769 )"_json;
770 std::unique_ptr<PMBusWriteVoutCommandAction> action =
771 parsePMBusWriteVoutCommand(element);
772 EXPECT_EQ(action->getVolts().has_value(), false);
773 EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
774 EXPECT_EQ(action->getExponent().has_value(), false);
775 EXPECT_EQ(action->isVerified(), false);
776 }
777
778 // Test where works: All properties specified
779 {
780 const json element = R"(
781 {
782 "volts": 1.03,
783 "format": "linear",
784 "exponent": -8,
785 "is_verified": true
786 }
787 )"_json;
788 std::unique_ptr<PMBusWriteVoutCommandAction> action =
789 parsePMBusWriteVoutCommand(element);
790 EXPECT_EQ(action->getVolts().has_value(), true);
791 EXPECT_EQ(action->getVolts().value(), 1.03);
792 EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear);
793 EXPECT_EQ(action->getExponent().has_value(), true);
794 EXPECT_EQ(action->getExponent().value(), -8);
795 EXPECT_EQ(action->isVerified(), true);
796 }
797
798 // Test where fails: Element is not an object
799 try
800 {
801 const json element = R"( [ "0xFF", "0x01" ] )"_json;
802 parsePMBusWriteVoutCommand(element);
803 ADD_FAILURE() << "Should not have reached this line.";
804 }
805 catch (const std::invalid_argument& e)
806 {
807 EXPECT_STREQ(e.what(), "Element is not an object");
808 }
809
810 // Test where fails: volts value is invalid
811 try
812 {
813 const json element = R"(
814 {
815 "volts": "foo",
816 "format": "linear"
817 }
818 )"_json;
819 parsePMBusWriteVoutCommand(element);
820 ADD_FAILURE() << "Should not have reached this line.";
821 }
822 catch (const std::invalid_argument& e)
823 {
824 EXPECT_STREQ(e.what(), "Element is not a number");
825 }
826
827 // Test where fails: Required format property not specified
828 try
829 {
830 const json element = R"(
831 {
832 "volts": 1.03,
833 "is_verified": true
834 }
835 )"_json;
836 parsePMBusWriteVoutCommand(element);
837 ADD_FAILURE() << "Should not have reached this line.";
838 }
839 catch (const std::invalid_argument& e)
840 {
841 EXPECT_STREQ(e.what(), "Required property missing: format");
842 }
843
844 // Test where fails: format value is invalid
845 try
846 {
847 const json element = R"(
848 {
849 "format": "linear_11"
850 }
851 )"_json;
852 parsePMBusWriteVoutCommand(element);
853 ADD_FAILURE() << "Should not have reached this line.";
854 }
855 catch (const std::invalid_argument& e)
856 {
857 EXPECT_STREQ(e.what(), "Invalid format value: linear_11");
858 }
859
860 // Test where fails: exponent value is invalid
861 try
862 {
863 const json element = R"(
864 {
865 "format": "linear",
866 "exponent": 1.3
867 }
868 )"_json;
869 parsePMBusWriteVoutCommand(element);
870 ADD_FAILURE() << "Should not have reached this line.";
871 }
872 catch (const std::invalid_argument& e)
873 {
874 EXPECT_STREQ(e.what(), "Element is not an integer");
875 }
876
877 // Test where fails: is_verified value is invalid
878 try
879 {
880 const json element = R"(
881 {
882 "format": "linear",
883 "is_verified": "true"
884 }
885 )"_json;
886 parsePMBusWriteVoutCommand(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 a boolean");
892 }
893
894 // Test where fails: Invalid property specified
895 try
896 {
897 const json element = R"(
898 {
899 "format": "linear",
900 "foo": "bar"
901 }
902 )"_json;
903 parsePMBusWriteVoutCommand(element);
904 ADD_FAILURE() << "Should not have reached this line.";
905 }
906 catch (const std::invalid_argument& e)
907 {
908 EXPECT_STREQ(e.what(), "Element contains an invalid property");
909 }
910}
911
912TEST(ConfigFileParserTests, ParseRoot)
913{
914 // Test where works: Only required properties specified
915 {
916 const json element = R"(
917 {
918 "chassis": [
919 { "number": 1 }
920 ]
921 }
922 )"_json;
923 std::vector<std::unique_ptr<Rule>> rules{};
924 std::vector<std::unique_ptr<Chassis>> chassis{};
925 std::tie(rules, chassis) = parseRoot(element);
926 EXPECT_EQ(rules.size(), 0);
927 // TODO: Not implemented yet
928 // EXPECT_EQ(chassis.size(), 1);
929 }
930
931 // Test where works: All properties specified
932 {
933 const json element = R"(
934 {
935 "comments": [ "Config file for a FooBar one-chassis system" ],
936 "rules": [
937 {
938 "id": "set_voltage_rule",
939 "actions": [
940 { "pmbus_write_vout_command": { "format": "linear" } }
941 ]
942 }
943 ],
944 "chassis": [
945 { "number": 1 },
946 { "number": 3 }
947 ]
948 }
949 )"_json;
950 std::vector<std::unique_ptr<Rule>> rules{};
951 std::vector<std::unique_ptr<Chassis>> chassis{};
952 std::tie(rules, chassis) = parseRoot(element);
953 EXPECT_EQ(rules.size(), 1);
954 // TODO: Not implemented yet
955 // EXPECT_EQ(chassis.size(), 2);
956 }
957
958 // Test where fails: Element is not an object
959 try
960 {
961 const json element = R"( [ "0xFF", "0x01" ] )"_json;
962 parseRoot(element);
963 ADD_FAILURE() << "Should not have reached this line.";
964 }
965 catch (const std::invalid_argument& e)
966 {
967 EXPECT_STREQ(e.what(), "Element is not an object");
968 }
969
970 // Test where fails: chassis property not specified
971 try
972 {
973 const json element = R"(
974 {
975 "rules": [
976 {
977 "id": "set_voltage_rule",
978 "actions": [
979 { "pmbus_write_vout_command": { "format": "linear" } }
980 ]
981 }
982 ]
983 }
984 )"_json;
985 parseRoot(element);
986 ADD_FAILURE() << "Should not have reached this line.";
987 }
988 catch (const std::invalid_argument& e)
989 {
990 EXPECT_STREQ(e.what(), "Required property missing: chassis");
991 }
992
993 // Test where fails: Invalid property specified
994 try
995 {
996 const json element = R"(
997 {
998 "remarks": [ "Config file for a FooBar one-chassis system" ],
999 "chassis": [
1000 { "number": 1 }
1001 ]
1002 }
1003 )"_json;
1004 parseRoot(element);
1005 ADD_FAILURE() << "Should not have reached this line.";
1006 }
1007 catch (const std::invalid_argument& e)
1008 {
1009 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1010 }
1011}
1012
1013TEST(ConfigFileParserTests, ParseRule)
1014{
1015 // Test where works: comments property specified
1016 {
1017 const json element = R"(
1018 {
1019 "comments": [ "Set voltage rule" ],
1020 "id": "set_voltage_rule",
1021 "actions": [
1022 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
1023 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }
1024 ]
1025 }
1026 )"_json;
1027 std::unique_ptr<Rule> rule = parseRule(element);
1028 EXPECT_EQ(rule->getID(), "set_voltage_rule");
1029 EXPECT_EQ(rule->getActions().size(), 2);
1030 }
1031
1032 // Test where works: comments property not specified
1033 {
1034 const json element = R"(
1035 {
1036 "id": "set_voltage_rule",
1037 "actions": [
1038 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
1039 { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } },
1040 { "pmbus_write_vout_command": { "volts": 1.05, "format": "linear" } }
1041 ]
1042 }
1043 )"_json;
1044 std::unique_ptr<Rule> rule = parseRule(element);
1045 EXPECT_EQ(rule->getID(), "set_voltage_rule");
1046 EXPECT_EQ(rule->getActions().size(), 3);
1047 }
1048
1049 // Test where fails: Element is not an object
1050 try
1051 {
1052 const json element = R"( [ "0xFF", "0x01" ] )"_json;
1053 parseRule(element);
1054 ADD_FAILURE() << "Should not have reached this line.";
1055 }
1056 catch (const std::invalid_argument& e)
1057 {
1058 EXPECT_STREQ(e.what(), "Element is not an object");
1059 }
1060
1061 // Test where fails: id property not specified
1062 try
1063 {
1064 const json element = R"(
1065 {
1066 "actions": [
1067 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
1068 ]
1069 }
1070 )"_json;
1071 parseRule(element);
1072 ADD_FAILURE() << "Should not have reached this line.";
1073 }
1074 catch (const std::invalid_argument& e)
1075 {
1076 EXPECT_STREQ(e.what(), "Required property missing: id");
1077 }
1078
1079 // Test where fails: id property is invalid
1080 try
1081 {
1082 const json element = R"(
1083 {
1084 "id": "",
1085 "actions": [
1086 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
1087 ]
1088 }
1089 )"_json;
1090 parseRule(element);
1091 ADD_FAILURE() << "Should not have reached this line.";
1092 }
1093 catch (const std::invalid_argument& e)
1094 {
1095 EXPECT_STREQ(e.what(), "Element contains an empty string");
1096 }
1097
1098 // Test where fails: actions property not specified
1099 try
1100 {
1101 const json element = R"(
1102 {
1103 "comments": [ "Set voltage rule" ],
1104 "id": "set_voltage_rule"
1105 }
1106 )"_json;
1107 parseRule(element);
1108 ADD_FAILURE() << "Should not have reached this line.";
1109 }
1110 catch (const std::invalid_argument& e)
1111 {
1112 EXPECT_STREQ(e.what(), "Required property missing: actions");
1113 }
1114
1115 // Test where fails: actions property is invalid
1116 try
1117 {
1118 const json element = R"(
1119 {
1120 "id": "set_voltage_rule",
1121 "actions": true
1122 }
1123 )"_json;
1124 parseRule(element);
1125 ADD_FAILURE() << "Should not have reached this line.";
1126 }
1127 catch (const std::invalid_argument& e)
1128 {
1129 EXPECT_STREQ(e.what(), "Element is not an array");
1130 }
1131
1132 // Test where fails: Invalid property specified
1133 try
1134 {
1135 const json element = R"(
1136 {
1137 "remarks": [ "Set voltage rule" ],
1138 "id": "set_voltage_rule",
1139 "actions": [
1140 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
1141 ]
1142 }
1143 )"_json;
1144 parseRule(element);
1145 ADD_FAILURE() << "Should not have reached this line.";
1146 }
1147 catch (const std::invalid_argument& e)
1148 {
1149 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1150 }
1151}
1152
1153TEST(ConfigFileParserTests, ParseRuleArray)
1154{
1155 // Test where works
1156 {
1157 const json element = R"(
1158 [
1159 {
1160 "id": "set_voltage_rule1",
1161 "actions": [
1162 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }
1163 ]
1164 },
1165 {
1166 "id": "set_voltage_rule2",
1167 "actions": [
1168 { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } },
1169 { "pmbus_write_vout_command": { "volts": 1.11, "format": "linear" } }
1170 ]
1171 }
1172 ]
1173 )"_json;
1174 std::vector<std::unique_ptr<Rule>> rules = parseRuleArray(element);
1175 EXPECT_EQ(rules.size(), 2);
1176 EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1");
1177 EXPECT_EQ(rules[0]->getActions().size(), 1);
1178 EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2");
1179 EXPECT_EQ(rules[1]->getActions().size(), 2);
1180 }
1181
1182 // Test where fails: Element is not an array
1183 try
1184 {
1185 const json element = R"( { "id": "set_voltage_rule" } )"_json;
1186 parseRuleArray(element);
1187 ADD_FAILURE() << "Should not have reached this line.";
1188 }
1189 catch (const std::invalid_argument& e)
1190 {
1191 EXPECT_STREQ(e.what(), "Element is not an array");
1192 }
1193}
1194
1195TEST(ConfigFileParserTests, ParseString)
1196{
1197 // Test where works: Empty string
1198 {
1199 const json element = "";
1200 std::string value = parseString(element, true);
1201 EXPECT_EQ(value, "");
1202 }
1203
1204 // Test where works: Non-empty string
1205 {
1206 const json element = "vdd_regulator";
1207 std::string value = parseString(element, false);
1208 EXPECT_EQ(value, "vdd_regulator");
1209 }
1210
1211 // Test where fails: Element is not a string
1212 try
1213 {
1214 const json element = R"( { "foo": "bar" } )"_json;
1215 parseString(element);
1216 ADD_FAILURE() << "Should not have reached this line.";
1217 }
1218 catch (const std::invalid_argument& e)
1219 {
1220 EXPECT_STREQ(e.what(), "Element is not a string");
1221 }
1222
1223 // Test where fails: Empty string
1224 try
1225 {
1226 const json element = "";
1227 parseString(element);
1228 ADD_FAILURE() << "Should not have reached this line.";
1229 }
1230 catch (const std::invalid_argument& e)
1231 {
1232 EXPECT_STREQ(e.what(), "Element contains an empty string");
1233 }
1234}
1235
Bob Kingf617f892020-03-30 19:03:35 +08001236TEST(ConfigFileParserTests, ParseStringToUint8)
1237{
1238 // Test where works: "0xFF"
1239 {
1240 const json element = R"( "0xFF" )"_json;
1241 uint8_t value = parseStringToUint8(element);
1242 EXPECT_EQ(value, 0xFF);
1243 }
1244
1245 // Test where works: "0xff"
1246 {
1247 const json element = R"( "0xff" )"_json;
1248 uint8_t value = parseStringToUint8(element);
1249 EXPECT_EQ(value, 0xff);
1250 }
1251
1252 // Test where works: "0xf"
1253 {
1254 const json element = R"( "0xf" )"_json;
1255 uint8_t value = parseStringToUint8(element);
1256 EXPECT_EQ(value, 0xf);
1257 }
1258
1259 // Test where fails: "0xfff"
1260 try
1261 {
1262 const json element = R"( "0xfff" )"_json;
1263 parseStringToUint8(element);
1264 ADD_FAILURE() << "Should not have reached this line.";
1265 }
1266 catch (const std::invalid_argument& e)
1267 {
1268 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1269 }
1270
1271 // Test where fails: "0xAG"
1272 try
1273 {
1274 const json element = R"( "0xAG" )"_json;
1275 parseStringToUint8(element);
1276 ADD_FAILURE() << "Should not have reached this line.";
1277 }
1278 catch (const std::invalid_argument& e)
1279 {
1280 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1281 }
1282
1283 // Test where fails: "ff"
1284 try
1285 {
1286 const json element = R"( "ff" )"_json;
1287 parseStringToUint8(element);
1288 ADD_FAILURE() << "Should not have reached this line.";
1289 }
1290 catch (const std::invalid_argument& e)
1291 {
1292 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1293 }
1294
1295 // Test where fails: ""
1296 try
1297 {
1298 const json element = "";
1299 parseStringToUint8(element);
1300 ADD_FAILURE() << "Should not have reached this line.";
1301 }
1302 catch (const std::invalid_argument& e)
1303 {
1304 EXPECT_STREQ(e.what(), "Element contains an empty string");
1305 }
1306
1307 // Test where fails: "f"
1308 try
1309 {
1310 const json element = R"( "f" )"_json;
1311 parseStringToUint8(element);
1312 ADD_FAILURE() << "Should not have reached this line.";
1313 }
1314 catch (const std::invalid_argument& e)
1315 {
1316 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1317 }
1318
1319 // Test where fails: "0x"
1320 try
1321 {
1322 const json element = R"( "0x" )"_json;
1323 parseStringToUint8(element);
1324 ADD_FAILURE() << "Should not have reached this line.";
1325 }
1326 catch (const std::invalid_argument& e)
1327 {
1328 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1329 }
1330
1331 // Test where fails: "0Xff"
1332 try
1333 {
1334 const json element = R"( "0XFF" )"_json;
1335 parseStringToUint8(element);
1336 ADD_FAILURE() << "Should not have reached this line.";
1337 }
1338 catch (const std::invalid_argument& e)
1339 {
1340 EXPECT_STREQ(e.what(), "Element is not hexadecimal string");
1341 }
1342}
1343
1344TEST(ConfigFileParserTests, ParseUint8)
1345{
1346 // Test where works: 0
1347 {
1348 const json element = R"( 0 )"_json;
1349 uint8_t value = parseUint8(element);
1350 EXPECT_EQ(value, 0);
1351 }
1352
1353 // Test where works: UINT8_MAX
1354 {
1355 const json element = R"( 255 )"_json;
1356 uint8_t value = parseUint8(element);
1357 EXPECT_EQ(value, 255);
1358 }
1359
1360 // Test where fails: Element is not an integer
1361 try
1362 {
1363 const json element = R"( 1.03 )"_json;
1364 parseUint8(element);
1365 ADD_FAILURE() << "Should not have reached this line.";
1366 }
1367 catch (const std::invalid_argument& e)
1368 {
1369 EXPECT_STREQ(e.what(), "Element is not an integer");
1370 }
1371
1372 // Test where fails: Value < 0
1373 try
1374 {
1375 const json element = R"( -1 )"_json;
1376 parseUint8(element);
1377 ADD_FAILURE() << "Should not have reached this line.";
1378 }
1379 catch (const std::invalid_argument& e)
1380 {
1381 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
1382 }
1383
1384 // Test where fails: Value > UINT8_MAX
1385 try
1386 {
1387 const json element = R"( 256 )"_json;
1388 parseUint8(element);
1389 ADD_FAILURE() << "Should not have reached this line.";
1390 }
1391 catch (const std::invalid_argument& e)
1392 {
1393 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
1394 }
1395}
1396
Shawn McCarney0e8c68a2020-03-27 01:44:48 -05001397TEST(ConfigFileParserTests, VerifyIsArray)
1398{
1399 // Test where element is an array
1400 try
1401 {
1402 const json element = R"( [ "foo", "bar" ] )"_json;
1403 verifyIsArray(element);
1404 }
1405 catch (const std::exception& e)
1406 {
1407 ADD_FAILURE() << "Should not have caught exception.";
1408 }
1409
1410 // Test where element is not an array
1411 try
1412 {
1413 const json element = R"( { "foo": "bar" } )"_json;
1414 verifyIsArray(element);
1415 ADD_FAILURE() << "Should not have reached this line.";
1416 }
1417 catch (const std::invalid_argument& e)
1418 {
1419 EXPECT_STREQ(e.what(), "Element is not an array");
1420 }
1421}
1422
1423TEST(ConfigFileParserTests, VerifyIsObject)
1424{
1425 // Test where element is an object
1426 try
1427 {
1428 const json element = R"( { "foo": "bar" } )"_json;
1429 verifyIsObject(element);
1430 }
1431 catch (const std::exception& e)
1432 {
1433 ADD_FAILURE() << "Should not have caught exception.";
1434 }
1435
1436 // Test where element is not an object
1437 try
1438 {
1439 const json element = R"( [ "foo", "bar" ] )"_json;
1440 verifyIsObject(element);
1441 ADD_FAILURE() << "Should not have reached this line.";
1442 }
1443 catch (const std::invalid_argument& e)
1444 {
1445 EXPECT_STREQ(e.what(), "Element is not an object");
1446 }
1447}
1448
1449TEST(ConfigFileParserTests, VerifyPropertyCount)
1450{
1451 // Test where element has expected number of properties
1452 try
1453 {
1454 const json element = R"(
1455 {
1456 "comments": [ "Set voltage rule" ],
1457 "id": "set_voltage_rule"
1458 }
1459 )"_json;
1460 verifyPropertyCount(element, 2);
1461 }
1462 catch (const std::exception& e)
1463 {
1464 ADD_FAILURE() << "Should not have caught exception.";
1465 }
1466
1467 // Test where element has unexpected number of properties
1468 try
1469 {
1470 const json element = R"(
1471 {
1472 "comments": [ "Set voltage rule" ],
1473 "id": "set_voltage_rule",
1474 "foo": 1.3
1475 }
1476 )"_json;
1477 verifyPropertyCount(element, 2);
1478 ADD_FAILURE() << "Should not have reached this line.";
1479 }
1480 catch (const std::invalid_argument& e)
1481 {
1482 EXPECT_STREQ(e.what(), "Element contains an invalid property");
1483 }
1484}