1 /**
2  * Copyright © 2019 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 "extensions/openpower-pels/registry.hpp"
17 
18 #include <nlohmann/json.hpp>
19 
20 #include <filesystem>
21 #include <fstream>
22 
23 #include <gtest/gtest.h>
24 
25 using namespace openpower::pels::message;
26 using namespace openpower::pels;
27 namespace fs = std::filesystem;
28 
29 const auto registryData = R"(
30 {
31     "PELs":
32     [
33         {
34             "Name": "xyz.openbmc_project.Power.Fault",
35             "Subsystem": "power_supply",
36 
37             "SRC":
38             {
39                 "ReasonCode": "0x2030"
40             },
41 
42             "Documentation":
43             {
44                 "Description": "A PGOOD Fault",
45                 "Message": "PS had a PGOOD Fault"
46             }
47         },
48 
49         {
50             "Name": "xyz.openbmc_project.Power.OverVoltage",
51             "Subsystem": "power_control_hw",
52             "Severity":
53             [
54                 {
55                     "System": "systemA",
56                     "SevValue": "unrecoverable"
57                 },
58                 {
59                     "System": "systemB",
60                     "SevValue": "recovered"
61                 },
62                 {
63                     "SevValue": "predictive"
64                 }
65             ],
66             "MfgSeverity": "non_error",
67             "ActionFlags": ["service_action", "report", "call_home"],
68             "MfgActionFlags": ["hidden"],
69 
70             "SRC":
71             {
72                 "ReasonCode": "0x2333",
73                 "Type": "BD",
74                 "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"],
75                 "Words6To9":
76                 {
77                     "6":
78                     {
79                         "Description": "Failing unit number",
80                         "AdditionalDataPropSource": "PS_NUM"
81                     },
82 
83                     "7":
84                     {
85                         "Description": "bad voltage",
86                         "AdditionalDataPropSource": "VOLTAGE"
87                     }
88                 },
89                 "DeconfigFlag": true,
90                 "CheckstopFlag": true
91             },
92 
93             "Documentation":
94             {
95                 "Description": "A PGOOD Fault",
96                 "Message": "PS %1 had a PGOOD Fault",
97                 "MessageArgSources":
98                 [
99                     "SRCWord6"
100                 ],
101                 "Notes": [
102                     "In the UserData section there is a JSON",
103                     "dump that provides debug information."
104                 ]
105             },
106 
107             "JournalCapture":
108             {
109                 "NumLines": 7
110             }
111         },
112 
113         {
114             "Name": "xyz.openbmc_project.Common.Error.Timeout",
115             "PossibleSubsystems": ["processor", "memory"],
116 
117             "SRC":
118             {
119                 "ReasonCode": "0x2030"
120             },
121             "Documentation":
122             {
123                 "Description": "A PGOOD Fault",
124                 "Message": "PS had a PGOOD Fault"
125             }
126         },
127 
128         {
129             "Name": "xyz.openbmc_project.Journal.Capture",
130             "Subsystem": "power_supply",
131 
132             "SRC":
133             {
134                 "ReasonCode": "0x2030"
135             },
136 
137             "Documentation":
138             {
139                 "Description": "journal capture test",
140                 "Message": "journal capture test"
141             },
142 
143             "JournalCapture":
144             {
145                 "Sections": [
146                     {
147                         "NumLines": 5,
148                         "SyslogID": "test1"
149                     },
150                     {
151                         "NumLines": 6,
152                         "SyslogID": "test2"
153                     }
154                 ]
155             }
156         },
157 
158         {
159             "Name": "org.open_power.PHAL.Info.ClockDailyLog",
160             "Subsystem": "cec_clocks",
161             "ComponentID": "0x3000",
162             "Severity": "non_error",
163             "ActionFlags": ["report", "call_home", "heartbeat_call_home"],
164 
165             "SRC":
166             {
167                 "ReasonCode": "0x300A",
168                 "Words6To9": {}
169             },
170 
171             "Documentation":
172             {
173                 "Description": "Informational error to house clock debug info",
174                 "Message": "Informational error to house clock debug info",
175                 "Notes": [
176                     "User data includes processor and clock register state information."
177                 ]
178             }
179         }
180     ]
181 }
182 )";
183 
184 class RegistryTest : public ::testing::Test
185 {
186   protected:
187     static void SetUpTestCase()
188     {
189         char path[] = "/tmp/regtestXXXXXX";
190         regDir = mkdtemp(path);
191     }
192 
193     static void TearDownTestCase()
194     {
195         fs::remove_all(regDir);
196     }
197 
198     static std::string writeData(const char* data)
199     {
200         fs::path path = regDir / "registry.json";
201         std::ofstream stream{path};
202         stream << data;
203         return path;
204     }
205 
206     static fs::path regDir;
207 };
208 
209 fs::path RegistryTest::regDir{};
210 
211 TEST_F(RegistryTest, TestNoEntry)
212 {
213     auto path = RegistryTest::writeData(registryData);
214     Registry registry{path};
215 
216     auto entry = registry.lookup("foo", LookupType::name);
217     EXPECT_FALSE(entry);
218 }
219 
220 TEST_F(RegistryTest, TestFindEntry)
221 {
222     auto path = RegistryTest::writeData(registryData);
223     Registry registry{path};
224 
225     auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
226                                  LookupType::name);
227     ASSERT_TRUE(entry);
228     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
229     EXPECT_EQ(entry->subsystem, 0x62);
230 
231     ASSERT_EQ(entry->severity->size(), 3);
232     EXPECT_EQ((*entry->severity)[0].severity, 0x40);
233     EXPECT_EQ((*entry->severity)[0].system, "systemA");
234     EXPECT_EQ((*entry->severity)[1].severity, 0x10);
235     EXPECT_EQ((*entry->severity)[1].system, "systemB");
236     EXPECT_EQ((*entry->severity)[2].severity, 0x20);
237     EXPECT_EQ((*entry->severity)[2].system, "");
238 
239     EXPECT_EQ(entry->mfgSeverity->size(), 1);
240     EXPECT_EQ((*entry->mfgSeverity)[0].severity, 0x00);
241 
242     EXPECT_EQ(*(entry->actionFlags), 0xA800);
243     EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
244     EXPECT_EQ(entry->componentID, 0x2300);
245     EXPECT_FALSE(entry->eventType);
246     EXPECT_FALSE(entry->eventScope);
247 
248     EXPECT_EQ(entry->src.type, 0xBD);
249     EXPECT_EQ(entry->src.reasonCode, 0x2333);
250     EXPECT_TRUE(entry->src.deconfigFlag);
251     EXPECT_TRUE(entry->src.checkstopFlag);
252 
253     auto& hexwords = entry->src.hexwordADFields;
254     EXPECT_TRUE(hexwords);
255     EXPECT_EQ((*hexwords).size(), 2);
256 
257     auto word = (*hexwords).find(6);
258     EXPECT_NE(word, (*hexwords).end());
259     EXPECT_EQ(std::get<0>(word->second), "PS_NUM");
260 
261     word = (*hexwords).find(7);
262     EXPECT_NE(word, (*hexwords).end());
263     EXPECT_EQ(std::get<0>(word->second), "VOLTAGE");
264 
265     auto& sid = entry->src.symptomID;
266     EXPECT_TRUE(sid);
267     EXPECT_EQ((*sid).size(), 3);
268     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
269     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
270     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
271 
272     EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
273     EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
274     auto& hexwordSource = entry->doc.messageArgSources;
275     EXPECT_TRUE(hexwordSource);
276     EXPECT_EQ((*hexwordSource).size(), 1);
277     EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
278 
279     const auto& jc = entry->journalCapture;
280     ASSERT_TRUE(jc);
281     ASSERT_TRUE(std::holds_alternative<size_t>(*jc));
282     EXPECT_EQ(std::get<size_t>(*jc), 7);
283 
284     entry = registry.lookup("0x2333", LookupType::reasonCode);
285     ASSERT_TRUE(entry);
286     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
287 
288     entry = registry.lookup("org.open_power.PHAL.Info.ClockDailyLog",
289                             LookupType::name);
290     ASSERT_TRUE(entry);
291     EXPECT_EQ(entry->name, "org.open_power.PHAL.Info.ClockDailyLog");
292     EXPECT_EQ(*(entry->actionFlags), 0x2820);
293 }
294 
295 // Check the entry that mostly uses defaults
296 TEST_F(RegistryTest, TestFindEntryMinimal)
297 {
298     auto path = RegistryTest::writeData(registryData);
299     Registry registry{path};
300 
301     auto entry =
302         registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name);
303     ASSERT_TRUE(entry);
304     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
305     EXPECT_EQ(entry->subsystem, 0x61);
306     EXPECT_FALSE(entry->severity);
307     EXPECT_FALSE(entry->mfgSeverity);
308     EXPECT_FALSE(entry->mfgActionFlags);
309     EXPECT_FALSE(entry->actionFlags);
310     EXPECT_EQ(entry->componentID, 0x2000);
311     EXPECT_FALSE(entry->eventType);
312     EXPECT_FALSE(entry->eventScope);
313 
314     EXPECT_EQ(entry->src.reasonCode, 0x2030);
315     EXPECT_EQ(entry->src.type, 0xBD);
316     EXPECT_FALSE(entry->src.hexwordADFields);
317     EXPECT_FALSE(entry->src.symptomID);
318     EXPECT_FALSE(entry->src.deconfigFlag);
319     EXPECT_FALSE(entry->src.checkstopFlag);
320 }
321 
322 TEST_F(RegistryTest, TestBadJSON)
323 {
324     auto path = RegistryTest::writeData("bad {} json");
325 
326     Registry registry{path};
327 
328     EXPECT_FALSE(registry.lookup("foo", LookupType::name));
329 }
330 
331 // Test the helper functions the use the pel_values data.
332 TEST_F(RegistryTest, TestHelperFunctions)
333 {
334     using namespace openpower::pels::message::helper;
335     EXPECT_EQ(getSubsystem("input_power_source"), 0xA1);
336     EXPECT_THROW(getSubsystem("foo"), std::runtime_error);
337 
338     EXPECT_EQ(getSeverity("symptom_recovered"), 0x71);
339     EXPECT_THROW(getSeverity("foo"), std::runtime_error);
340 
341     EXPECT_EQ(getEventType("dump_notification"), 0x08);
342     EXPECT_THROW(getEventType("foo"), std::runtime_error);
343 
344     EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04);
345     EXPECT_THROW(getEventScope("foo"), std::runtime_error);
346 
347     std::vector<std::string> flags{"service_action", "dont_report",
348                                    "termination"};
349     EXPECT_EQ(getActionFlags(flags), 0x9100);
350 
351     flags.push_back("heartbeat_call_home");
352     EXPECT_EQ(getActionFlags(flags), 0x9120);
353     flags.clear();
354     flags.push_back("heartbeat_call_home");
355     EXPECT_EQ(getActionFlags(flags), 0x0020);
356 
357     flags.clear();
358     flags.push_back("foo");
359     EXPECT_THROW(getActionFlags(flags), std::runtime_error);
360 }
361 
362 TEST_F(RegistryTest, TestGetSRCReasonCode)
363 {
364     using namespace openpower::pels::message::helper;
365     EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
366               0x5555);
367 
368     EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
369                  std::runtime_error);
370 }
371 
372 TEST_F(RegistryTest, TestGetSRCType)
373 {
374     using namespace openpower::pels::message::helper;
375     EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
376     EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
377 
378     EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
379                  std::runtime_error);
380 
381     EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
382                  std::runtime_error);
383 }
384 
385 TEST_F(RegistryTest, TestGetSRCHexwordFields)
386 {
387     using namespace openpower::pels::message::helper;
388     const auto hexwords = R"(
389     {"Words6To9":
390       {
391         "8":
392         {
393             "Description": "TEST",
394             "AdditionalDataPropSource": "TEST"
395         }
396       }
397     })"_json;
398 
399     auto fields = getSRCHexwordFields(hexwords, "foo");
400     EXPECT_TRUE(fields);
401     auto word = fields->find(8);
402     EXPECT_NE(word, fields->end());
403 
404     const auto theInvalidRWord = R"(
405     {"Words6To9":
406       {
407         "R":
408         {
409             "Description": "TEST",
410             "AdditionalDataPropSource": "TEST"
411         }
412       }
413     })"_json;
414 
415     EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
416                  std::runtime_error);
417 }
418 
419 TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
420 {
421     using namespace openpower::pels::message::helper;
422     const auto sID = R"(
423     {
424         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
425     })"_json;
426 
427     auto fields = getSRCSymptomIDFields(sID, "foo");
428     EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
429     EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
430     EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
431 
432     const auto badField = R"(
433     {
434         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
435     })"_json;
436 
437     EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
438 }
439 
440 TEST_F(RegistryTest, TestGetComponentID)
441 {
442     using namespace openpower::pels::message::helper;
443 
444     // Get it from the JSON
445     auto id =
446         getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo");
447     EXPECT_EQ(id, 0x4200);
448 
449     // Get it from the reason code on a 0xBD SRC
450     id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
451     EXPECT_EQ(id, 0x6700);
452 
453     // Not present on a 0x11 SRC
454     EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
455                  std::runtime_error);
456 }
457 
458 // Test when callouts are in the JSON.
459 TEST_F(RegistryTest, TestGetCallouts)
460 {
461     std::vector<std::string> systemNames;
462 
463     {
464         // Callouts without AD, that depend on system type,
465         // where there is a default entry without a system type.
466         auto json = R"(
467         [
468         {
469             "System": "system1",
470             "CalloutList":
471             [
472                 {
473                     "Priority": "high",
474                     "LocCode": "P1-C1"
475                 },
476                 {
477                     "Priority": "low",
478                     "LocCode": "P1"
479                 },
480                 {
481                     "Priority": "low",
482                     "SymbolicFRU": "service_docs"
483                 },
484                 {
485                     "Priority": "low",
486                     "SymbolicFRUTrusted": "air_mover",
487                     "UseInventoryLocCode": true
488                 }
489             ]
490         },
491         {
492             "CalloutList":
493             [
494                 {
495                     "Priority": "medium",
496                     "Procedure": "BMC0001"
497                 },
498                 {
499                     "Priority": "low",
500                     "LocCode": "P3-C8",
501                     "SymbolicFRUTrusted": "service_docs"
502                 }
503             ]
504 
505         }
506         ])"_json;
507 
508         AdditionalData ad;
509         systemNames.push_back("system1");
510 
511         auto callouts = Registry::getCallouts(json, systemNames, ad);
512         EXPECT_EQ(callouts.size(), 4);
513         EXPECT_EQ(callouts[0].priority, "high");
514         EXPECT_EQ(callouts[0].locCode, "P1-C1");
515         EXPECT_EQ(callouts[0].procedure, "");
516         EXPECT_EQ(callouts[0].symbolicFRU, "");
517         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
518         EXPECT_EQ(callouts[1].priority, "low");
519         EXPECT_EQ(callouts[1].locCode, "P1");
520         EXPECT_EQ(callouts[1].procedure, "");
521         EXPECT_EQ(callouts[1].symbolicFRU, "");
522         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
523         EXPECT_EQ(callouts[2].priority, "low");
524         EXPECT_EQ(callouts[2].locCode, "");
525         EXPECT_EQ(callouts[2].procedure, "");
526         EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
527         EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
528         EXPECT_EQ(callouts[3].priority, "low");
529         EXPECT_EQ(callouts[3].locCode, "");
530         EXPECT_EQ(callouts[3].procedure, "");
531         EXPECT_EQ(callouts[3].symbolicFRU, "");
532         EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
533         EXPECT_EQ(callouts[3].useInventoryLocCode, true);
534 
535         // system2 isn't in the JSON, so it will pick the default one
536         systemNames[0] = "system2";
537         callouts = Registry::getCallouts(json, systemNames, ad);
538         EXPECT_EQ(callouts.size(), 2);
539         EXPECT_EQ(callouts[0].priority, "medium");
540         EXPECT_EQ(callouts[0].locCode, "");
541         EXPECT_EQ(callouts[0].procedure, "BMC0001");
542         EXPECT_EQ(callouts[0].symbolicFRU, "");
543         EXPECT_EQ(callouts[1].priority, "low");
544         EXPECT_EQ(callouts[1].locCode, "P3-C8");
545         EXPECT_EQ(callouts[1].procedure, "");
546         EXPECT_EQ(callouts[1].symbolicFRU, "");
547         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
548         EXPECT_EQ(callouts[1].useInventoryLocCode, false);
549     }
550 
551     // Empty JSON array (treated as an error)
552     {
553         auto json = R"([])"_json;
554         AdditionalData ad;
555         systemNames[0] = "system1";
556         EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
557                      std::runtime_error);
558     }
559 
560     {
561         // Callouts without AD, that depend on system type,
562         // where there isn't a default entry without a system type.
563         auto json = R"(
564         [
565         {
566             "System": "system1",
567             "CalloutList":
568             [
569                 {
570                     "Priority": "high",
571                     "LocCode": "P1-C1"
572                 },
573                 {
574                     "Priority": "low",
575                     "LocCode": "P1",
576                     "SymbolicFRU": "1234567"
577                 }
578             ]
579         },
580         {
581             "System": "system2",
582             "CalloutList":
583             [
584                 {
585                     "Priority": "medium",
586                     "LocCode": "P7",
587                     "CalloutType": "tool_fru"
588                 }
589             ]
590 
591         }
592         ])"_json;
593 
594         AdditionalData ad;
595         systemNames[0] = "system1";
596 
597         auto callouts = Registry::getCallouts(json, systemNames, ad);
598         EXPECT_EQ(callouts.size(), 2);
599         EXPECT_EQ(callouts[0].priority, "high");
600         EXPECT_EQ(callouts[0].locCode, "P1-C1");
601         EXPECT_EQ(callouts[0].procedure, "");
602         EXPECT_EQ(callouts[0].symbolicFRU, "");
603         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
604         EXPECT_EQ(callouts[1].priority, "low");
605         EXPECT_EQ(callouts[1].locCode, "P1");
606         EXPECT_EQ(callouts[1].procedure, "");
607         EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
608         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
609 
610         systemNames[0] = "system2";
611         callouts = Registry::getCallouts(json, systemNames, ad);
612         EXPECT_EQ(callouts.size(), 1);
613         EXPECT_EQ(callouts[0].priority, "medium");
614         EXPECT_EQ(callouts[0].locCode, "P7");
615         EXPECT_EQ(callouts[0].procedure, "");
616         EXPECT_EQ(callouts[0].symbolicFRU, "");
617         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
618 
619         // There is no entry for system3 or a default system,
620         // so this should fail.
621         systemNames[0] = "system3";
622         EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
623                      std::runtime_error);
624     }
625 
626     {
627         // Callouts that use the AdditionalData key PROC_NUM
628         // as an index into them, along with a system type.
629         // It supports PROC_NUMs 0 and 1.
630         auto json = R"(
631         {
632             "ADName": "PROC_NUM",
633             "CalloutsWithTheirADValues":
634             [
635                 {
636                     "ADValue": "0",
637                     "Callouts":
638                     [
639                         {
640                             "System": "system3",
641                             "CalloutList":
642                             [
643                                 {
644                                     "Priority": "high",
645                                     "LocCode": "P1-C5"
646                                 },
647                                 {
648                                     "Priority": "medium",
649                                     "LocCode": "P1-C6",
650                                     "SymbolicFRU": "1234567"
651                                 },
652                                 {
653                                     "Priority": "low",
654                                     "Procedure": "BMC0001",
655                                     "CalloutType": "config_procedure"
656                                 }
657                             ]
658                         },
659                         {
660                             "CalloutList":
661                             [
662                                 {
663                                     "Priority": "low",
664                                     "LocCode": "P55"
665                                 }
666                             ]
667                         }
668                     ]
669                 },
670                 {
671                     "ADValue": "1",
672                     "Callouts":
673                     [
674                         {
675                             "CalloutList":
676                             [
677                                 {
678                                     "Priority": "high",
679                                     "LocCode": "P1-C6",
680                                     "CalloutType": "external_fru"
681                                 }
682                             ]
683                         }
684                     ]
685                 }
686             ]
687         })"_json;
688 
689         {
690             // Find callouts for PROC_NUM 0 on system3
691             std::vector<std::string> adData{"PROC_NUM=0"};
692             AdditionalData ad{adData};
693             systemNames[0] = "system3";
694 
695             auto callouts = Registry::getCallouts(json, systemNames, ad);
696             EXPECT_EQ(callouts.size(), 3);
697             EXPECT_EQ(callouts[0].priority, "high");
698             EXPECT_EQ(callouts[0].locCode, "P1-C5");
699             EXPECT_EQ(callouts[0].procedure, "");
700             EXPECT_EQ(callouts[0].symbolicFRU, "");
701             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
702             EXPECT_EQ(callouts[1].priority, "medium");
703             EXPECT_EQ(callouts[1].locCode, "P1-C6");
704             EXPECT_EQ(callouts[1].procedure, "");
705             EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
706             EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
707             EXPECT_EQ(callouts[2].priority, "low");
708             EXPECT_EQ(callouts[2].locCode, "");
709             EXPECT_EQ(callouts[2].procedure, "BMC0001");
710             EXPECT_EQ(callouts[2].symbolicFRU, "");
711             EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
712 
713             // Find callouts for PROC_NUM 0 that uses the default system entry.
714             systemNames[0] = "system99";
715 
716             callouts = Registry::getCallouts(json, systemNames, ad);
717             EXPECT_EQ(callouts.size(), 1);
718             EXPECT_EQ(callouts[0].priority, "low");
719             EXPECT_EQ(callouts[0].locCode, "P55");
720             EXPECT_EQ(callouts[0].procedure, "");
721             EXPECT_EQ(callouts[0].symbolicFRU, "");
722             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
723         }
724         {
725             // Find callouts for PROC_NUM 1 that uses a default system entry.
726             std::vector<std::string> adData{"PROC_NUM=1"};
727             AdditionalData ad{adData};
728             systemNames[0] = "system1";
729 
730             auto callouts = Registry::getCallouts(json, systemNames, ad);
731             EXPECT_EQ(callouts.size(), 1);
732             EXPECT_EQ(callouts[0].priority, "high");
733             EXPECT_EQ(callouts[0].locCode, "P1-C6");
734             EXPECT_EQ(callouts[0].procedure, "");
735             EXPECT_EQ(callouts[0].symbolicFRU, "");
736             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
737         }
738         {
739             // There is no entry for PROC_NUM 2, so no callouts
740             std::vector<std::string> adData{"PROC_NUM=2"};
741             AdditionalData ad{adData};
742 
743             auto callouts = Registry::getCallouts(json, systemNames, ad);
744             EXPECT_TRUE(callouts.empty());
745         }
746     }
747 
748     {
749         // Callouts with a 'CalloutsWhenNoADMatch' section that will
750         // be used when the AdditionalData value doesn't match.
751         auto json = R"(
752         {
753             "ADName": "PROC_NUM",
754             "CalloutsWithTheirADValues":
755             [
756                 {
757                     "ADValue": "0",
758                     "Callouts":
759                     [
760                         {
761                             "CalloutList":
762                             [
763                                 {
764                                     "Priority": "high",
765                                     "LocCode": "P0-C0"
766                                 }
767                             ]
768                         }
769                     ]
770                 }
771             ],
772             "CalloutsWhenNoADMatch": [
773                 {
774                     "CalloutList": [
775                         {
776                             "Priority": "medium",
777                             "LocCode": "P1-C1"
778                         }
779                     ]
780                 }
781             ]
782         })"_json;
783 
784         // There isn't an entry in the JSON for a PROC_NUM of 8
785         // so it should choose the P1-C1 callout.
786         std::vector<std::string> adData{"PROC_NUM=8"};
787         AdditionalData ad{adData};
788         systemNames.clear();
789 
790         auto callouts = Registry::getCallouts(json, systemNames, ad);
791         EXPECT_EQ(callouts.size(), 1);
792         EXPECT_EQ(callouts[0].priority, "medium");
793         EXPECT_EQ(callouts[0].locCode, "P1-C1");
794     }
795 }
796 
797 TEST_F(RegistryTest, TestGetCalloutsWithSystems)
798 {
799     std::vector<std::string> systemNames;
800 
801     auto json = R"(
802         [
803         {
804             "Systems": ["system1", "system2"],
805             "CalloutList":
806             [
807                 {
808                     "Priority": "high",
809                     "LocCode": "P1-C1"
810                 },
811                 {
812                     "Priority": "low",
813                     "LocCode": "P1"
814                 },
815                 {
816                     "Priority": "low",
817                     "SymbolicFRU": "service_docs"
818                 },
819                 {
820                     "Priority": "low",
821                     "SymbolicFRUTrusted": "air_mover",
822                     "UseInventoryLocCode": true
823                 }
824             ]
825         },
826         {
827             "CalloutList":
828             [
829                 {
830                     "Priority": "medium",
831                     "Procedure": "BMC0001"
832                 },
833                 {
834                     "Priority": "low",
835                     "LocCode": "P3-C8",
836                     "SymbolicFRUTrusted": "service_docs"
837                 }
838             ]
839 
840         }
841         ])"_json;
842 
843     AdditionalData ad;
844     systemNames.push_back("system1");
845 
846     auto callouts = Registry::getCallouts(json, systemNames, ad);
847     EXPECT_EQ(callouts.size(), 4);
848     EXPECT_EQ(callouts[0].priority, "high");
849     EXPECT_EQ(callouts[0].locCode, "P1-C1");
850     EXPECT_EQ(callouts[0].procedure, "");
851     EXPECT_EQ(callouts[0].symbolicFRU, "");
852     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
853     EXPECT_EQ(callouts[1].priority, "low");
854     EXPECT_EQ(callouts[1].locCode, "P1");
855     EXPECT_EQ(callouts[1].procedure, "");
856     EXPECT_EQ(callouts[1].symbolicFRU, "");
857     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
858     EXPECT_EQ(callouts[2].priority, "low");
859     EXPECT_EQ(callouts[2].locCode, "");
860     EXPECT_EQ(callouts[2].procedure, "");
861     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
862     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
863     EXPECT_EQ(callouts[3].priority, "low");
864     EXPECT_EQ(callouts[3].locCode, "");
865     EXPECT_EQ(callouts[3].procedure, "");
866     EXPECT_EQ(callouts[3].symbolicFRU, "");
867     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
868     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
869 
870     // System3 isn't in the JSON, so it will pick the default one
871     systemNames[0] = "system3";
872 
873     callouts = Registry::getCallouts(json, systemNames, ad);
874     EXPECT_EQ(callouts.size(), 2);
875     EXPECT_EQ(callouts[0].priority, "medium");
876     EXPECT_EQ(callouts[0].locCode, "");
877     EXPECT_EQ(callouts[0].procedure, "BMC0001");
878     EXPECT_EQ(callouts[0].symbolicFRU, "");
879     EXPECT_EQ(callouts[1].priority, "low");
880     EXPECT_EQ(callouts[1].locCode, "P3-C8");
881     EXPECT_EQ(callouts[1].procedure, "");
882     EXPECT_EQ(callouts[1].symbolicFRU, "");
883     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
884     EXPECT_EQ(callouts[1].useInventoryLocCode, false);
885 }
886 
887 TEST_F(RegistryTest, TestGetCalloutsWithSystemAndSystems)
888 {
889     std::vector<std::string> systemNames;
890 
891     auto json = R"(
892         [
893         {
894             "Systems": ["system1", "system2"],
895             "CalloutList":
896             [
897                 {
898                     "Priority": "high",
899                     "LocCode": "P1-C1"
900                 },
901                 {
902                     "Priority": "low",
903                     "LocCode": "P1"
904                 }
905             ]
906         },
907         {
908             "System": "system1",
909             "CalloutList":
910             [
911                 {
912                     "Priority": "low",
913                     "SymbolicFRU": "service_docs"
914                 },
915                 {
916                     "Priority": "low",
917                     "SymbolicFRUTrusted": "air_mover",
918                     "UseInventoryLocCode": true
919                 }
920             ]
921         },
922         {
923             "CalloutList":
924             [
925                 {
926                     "Priority": "medium",
927                     "Procedure": "BMC0001"
928                 },
929                 {
930                     "Priority": "low",
931                     "LocCode": "P3-C8",
932                     "SymbolicFRUTrusted": "service_docs"
933                 }
934             ]
935         }
936         ])"_json;
937 
938     AdditionalData ad;
939     systemNames.push_back("system1");
940 
941     auto callouts = Registry::getCallouts(json, systemNames, ad);
942     EXPECT_EQ(callouts.size(), 4);
943     EXPECT_EQ(callouts[0].priority, "high");
944     EXPECT_EQ(callouts[0].locCode, "P1-C1");
945     EXPECT_EQ(callouts[0].procedure, "");
946     EXPECT_EQ(callouts[0].symbolicFRU, "");
947     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
948     EXPECT_EQ(callouts[1].priority, "low");
949     EXPECT_EQ(callouts[1].locCode, "P1");
950     EXPECT_EQ(callouts[1].procedure, "");
951     EXPECT_EQ(callouts[1].symbolicFRU, "");
952     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
953     EXPECT_EQ(callouts[2].priority, "low");
954     EXPECT_EQ(callouts[2].locCode, "");
955     EXPECT_EQ(callouts[2].procedure, "");
956     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
957     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
958     EXPECT_EQ(callouts[3].priority, "low");
959     EXPECT_EQ(callouts[3].locCode, "");
960     EXPECT_EQ(callouts[3].procedure, "");
961     EXPECT_EQ(callouts[3].symbolicFRU, "");
962     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
963     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
964 
965     // if system name is "System2"
966     systemNames[0] = "system2";
967 
968     callouts = Registry::getCallouts(json, systemNames, ad);
969     EXPECT_EQ(callouts.size(), 2);
970     EXPECT_EQ(callouts[0].priority, "high");
971     EXPECT_EQ(callouts[0].locCode, "P1-C1");
972     EXPECT_EQ(callouts[0].procedure, "");
973     EXPECT_EQ(callouts[0].symbolicFRU, "");
974     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
975     EXPECT_EQ(callouts[1].priority, "low");
976     EXPECT_EQ(callouts[1].locCode, "P1");
977     EXPECT_EQ(callouts[1].procedure, "");
978     EXPECT_EQ(callouts[1].symbolicFRU, "");
979     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
980 
981     // system name is System3 which is not in json thereby will take default
982     systemNames[0] = "system3";
983 
984     callouts = Registry::getCallouts(json, systemNames, ad);
985     EXPECT_EQ(callouts.size(), 2);
986     EXPECT_EQ(callouts[0].priority, "medium");
987     EXPECT_EQ(callouts[0].locCode, "");
988     EXPECT_EQ(callouts[0].procedure, "BMC0001");
989     EXPECT_EQ(callouts[0].symbolicFRU, "");
990     EXPECT_EQ(callouts[1].priority, "low");
991     EXPECT_EQ(callouts[1].locCode, "P3-C8");
992     EXPECT_EQ(callouts[1].procedure, "");
993     EXPECT_EQ(callouts[1].symbolicFRU, "");
994     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
995     EXPECT_EQ(callouts[1].useInventoryLocCode, false);
996 }
997 
998 TEST_F(RegistryTest, TestGetCalloutsWithOnlySystemAndSystems)
999 {
1000     std::vector<std::string> systemNames;
1001 
1002     auto json = R"(
1003         [
1004         {
1005             "Systems": ["system1", "system2"],
1006             "CalloutList":
1007             [
1008                 {
1009                     "Priority": "high",
1010                     "LocCode": "P1-C1"
1011                 },
1012                 {
1013                     "Priority": "low",
1014                     "LocCode": "P1"
1015                 }
1016             ]
1017         },
1018         {
1019             "System": "system1",
1020             "CalloutList":
1021             [
1022                 {
1023                     "Priority": "low",
1024                     "SymbolicFRU": "service_docs"
1025                 },
1026                 {
1027                     "Priority": "low",
1028                     "SymbolicFRUTrusted": "air_mover",
1029                     "UseInventoryLocCode": true
1030                 }
1031             ]
1032         }
1033         ])"_json;
1034 
1035     AdditionalData ad;
1036     systemNames.push_back("system1");
1037 
1038     auto callouts = Registry::getCallouts(json, systemNames, ad);
1039     EXPECT_EQ(callouts.size(), 4);
1040     EXPECT_EQ(callouts[0].priority, "high");
1041     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1042     EXPECT_EQ(callouts[0].procedure, "");
1043     EXPECT_EQ(callouts[0].symbolicFRU, "");
1044     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1045     EXPECT_EQ(callouts[1].priority, "low");
1046     EXPECT_EQ(callouts[1].locCode, "P1");
1047     EXPECT_EQ(callouts[1].procedure, "");
1048     EXPECT_EQ(callouts[1].symbolicFRU, "");
1049     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1050     EXPECT_EQ(callouts[2].priority, "low");
1051     EXPECT_EQ(callouts[2].locCode, "");
1052     EXPECT_EQ(callouts[2].procedure, "");
1053     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
1054     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
1055     EXPECT_EQ(callouts[3].priority, "low");
1056     EXPECT_EQ(callouts[3].locCode, "");
1057     EXPECT_EQ(callouts[3].procedure, "");
1058     EXPECT_EQ(callouts[3].symbolicFRU, "");
1059     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
1060     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
1061 
1062     // if system name is "System2"
1063     systemNames[0] = "system2";
1064 
1065     callouts = Registry::getCallouts(json, systemNames, ad);
1066     EXPECT_EQ(callouts.size(), 2);
1067     EXPECT_EQ(callouts[0].priority, "high");
1068     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1069     EXPECT_EQ(callouts[0].procedure, "");
1070     EXPECT_EQ(callouts[0].symbolicFRU, "");
1071     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1072     EXPECT_EQ(callouts[1].priority, "low");
1073     EXPECT_EQ(callouts[1].locCode, "P1");
1074     EXPECT_EQ(callouts[1].procedure, "");
1075     EXPECT_EQ(callouts[1].symbolicFRU, "");
1076     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1077 
1078     // There is no entry for system3 or a default system,
1079     // so this should fail.
1080     systemNames[0] = "system3";
1081     EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
1082                  std::runtime_error);
1083 }
1084 
1085 TEST_F(RegistryTest, TestGetCalloutsWithOnlySystems)
1086 {
1087     std::vector<std::string> systemNames;
1088 
1089     auto json = R"(
1090         [
1091         {
1092             "Systems": ["system1", "system2"],
1093             "CalloutList":
1094             [
1095                 {
1096                     "Priority": "high",
1097                     "LocCode": "P1-C1"
1098                 },
1099                 {
1100                     "Priority": "low",
1101                     "LocCode": "P1"
1102                 }
1103             ]
1104         }
1105         ])"_json;
1106 
1107     AdditionalData ad;
1108     systemNames.push_back("system1");
1109 
1110     // system1 is available in JSON array
1111     auto callouts = Registry::getCallouts(json, systemNames, ad);
1112     EXPECT_EQ(callouts.size(), 2);
1113     EXPECT_EQ(callouts[0].priority, "high");
1114     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1115     EXPECT_EQ(callouts[0].procedure, "");
1116     EXPECT_EQ(callouts[0].symbolicFRU, "");
1117     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1118     EXPECT_EQ(callouts[1].priority, "low");
1119     EXPECT_EQ(callouts[1].locCode, "P1");
1120     EXPECT_EQ(callouts[1].procedure, "");
1121     EXPECT_EQ(callouts[1].symbolicFRU, "");
1122     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1123 
1124     // There is no entry for system3 or a default system,
1125     // so this should fail.
1126     systemNames[0] = "system3";
1127     EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
1128                  std::runtime_error);
1129 }
1130 
1131 TEST_F(RegistryTest, TestGetCalloutsWithOnlyDefaults)
1132 {
1133     std::vector<std::string> systemNames;
1134 
1135     auto json = R"(
1136         [
1137         {
1138             "CalloutList":
1139             [
1140                 {
1141                     "Priority": "high",
1142                     "LocCode": "P1-C1"
1143                 },
1144                 {
1145                     "Priority": "low",
1146                     "LocCode": "P1"
1147                 }
1148             ]
1149         }
1150         ])"_json;
1151 
1152     AdditionalData ad;
1153     systemNames.push_back("system1");
1154 
1155     // Since neither System or Systems available, it will pick the default one
1156     // only
1157     auto callouts = Registry::getCallouts(json, systemNames, ad);
1158     EXPECT_EQ(callouts.size(), 2);
1159     EXPECT_EQ(callouts[0].priority, "high");
1160     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1161     EXPECT_EQ(callouts[0].procedure, "");
1162     EXPECT_EQ(callouts[0].symbolicFRU, "");
1163     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1164     EXPECT_EQ(callouts[1].priority, "low");
1165     EXPECT_EQ(callouts[1].locCode, "P1");
1166     EXPECT_EQ(callouts[1].procedure, "");
1167     EXPECT_EQ(callouts[1].symbolicFRU, "");
1168     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1169 }
1170 
1171 TEST_F(RegistryTest, TestNoSubsystem)
1172 {
1173     auto path = RegistryTest::writeData(registryData);
1174     Registry registry{path};
1175 
1176     auto entry = registry.lookup("xyz.openbmc_project.Common.Error.Timeout",
1177                                  LookupType::name);
1178     ASSERT_TRUE(entry);
1179     EXPECT_FALSE(entry->subsystem);
1180 }
1181 
1182 TEST_F(RegistryTest, TestJournalSectionCapture)
1183 {
1184     auto path = RegistryTest::writeData(registryData);
1185     Registry registry{path};
1186 
1187     auto entry = registry.lookup("xyz.openbmc_project.Journal.Capture",
1188                                  LookupType::name);
1189     ASSERT_TRUE(entry);
1190 
1191     const auto& jc = entry->journalCapture;
1192     ASSERT_TRUE(jc);
1193     ASSERT_TRUE(std::holds_alternative<AppCaptureList>(*jc));
1194     const auto& acl = std::get<AppCaptureList>(*jc);
1195 
1196     ASSERT_EQ(acl.size(), 2);
1197 
1198     EXPECT_EQ(acl[0].syslogID, "test1");
1199     EXPECT_EQ(acl[0].numLines, 5);
1200 
1201     EXPECT_EQ(acl[1].syslogID, "test2");
1202     EXPECT_EQ(acl[1].numLines, 6);
1203 }
1204