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