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             },
90 
91             "Documentation":
92             {
93                 "Description": "A PGOOD Fault",
94                 "Message": "PS %1 had a PGOOD Fault",
95                 "MessageArgSources":
96                 [
97                     "SRCWord6"
98                 ],
99                 "Notes": [
100                     "In the UserData section there is a JSON",
101                     "dump that provides debug information."
102                 ]
103             },
104 
105             "JournalCapture":
106             {
107                 "NumLines": 7
108             }
109         },
110 
111         {
112             "Name": "xyz.openbmc_project.Common.Error.Timeout",
113             "PossibleSubsystems": ["processor", "memory"],
114 
115             "SRC":
116             {
117                 "ReasonCode": "0x2030"
118             },
119             "Documentation":
120             {
121                 "Description": "A PGOOD Fault",
122                 "Message": "PS had a PGOOD Fault"
123             }
124         },
125 
126         {
127             "Name": "xyz.openbmc_project.Journal.Capture",
128             "Subsystem": "power_supply",
129 
130             "SRC":
131             {
132                 "ReasonCode": "0x2030"
133             },
134 
135             "Documentation":
136             {
137                 "Description": "journal capture test",
138                 "Message": "journal capture test"
139             },
140 
141             "JournalCapture":
142             {
143                 "Sections": [
144                     {
145                         "NumLines": 5,
146                         "SyslogID": "test1"
147                     },
148                     {
149                         "NumLines": 6,
150                         "SyslogID": "test2"
151                     }
152                 ]
153             }
154         }
155     ]
156 }
157 )";
158 
159 class RegistryTest : public ::testing::Test
160 {
161   protected:
162     static void SetUpTestCase()
163     {
164         char path[] = "/tmp/regtestXXXXXX";
165         regDir = mkdtemp(path);
166     }
167 
168     static void TearDownTestCase()
169     {
170         fs::remove_all(regDir);
171     }
172 
173     static std::string writeData(const char* data)
174     {
175         fs::path path = regDir / "registry.json";
176         std::ofstream stream{path};
177         stream << data;
178         return path;
179     }
180 
181     static fs::path regDir;
182 };
183 
184 fs::path RegistryTest::regDir{};
185 
186 TEST_F(RegistryTest, TestNoEntry)
187 {
188     auto path = RegistryTest::writeData(registryData);
189     Registry registry{path};
190 
191     auto entry = registry.lookup("foo", LookupType::name);
192     EXPECT_FALSE(entry);
193 }
194 
195 TEST_F(RegistryTest, TestFindEntry)
196 {
197     auto path = RegistryTest::writeData(registryData);
198     Registry registry{path};
199 
200     auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
201                                  LookupType::name);
202     ASSERT_TRUE(entry);
203     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
204     EXPECT_EQ(entry->subsystem, 0x62);
205 
206     ASSERT_EQ(entry->severity->size(), 3);
207     EXPECT_EQ((*entry->severity)[0].severity, 0x40);
208     EXPECT_EQ((*entry->severity)[0].system, "systemA");
209     EXPECT_EQ((*entry->severity)[1].severity, 0x10);
210     EXPECT_EQ((*entry->severity)[1].system, "systemB");
211     EXPECT_EQ((*entry->severity)[2].severity, 0x20);
212     EXPECT_EQ((*entry->severity)[2].system, "");
213 
214     EXPECT_EQ(entry->mfgSeverity->size(), 1);
215     EXPECT_EQ((*entry->mfgSeverity)[0].severity, 0x00);
216 
217     EXPECT_EQ(*(entry->actionFlags), 0xA800);
218     EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
219     EXPECT_EQ(entry->componentID, 0x2300);
220     EXPECT_FALSE(entry->eventType);
221     EXPECT_FALSE(entry->eventScope);
222 
223     EXPECT_EQ(entry->src.type, 0xBD);
224     EXPECT_EQ(entry->src.reasonCode, 0x2333);
225 
226     auto& hexwords = entry->src.hexwordADFields;
227     EXPECT_TRUE(hexwords);
228     EXPECT_EQ((*hexwords).size(), 2);
229 
230     auto word = (*hexwords).find(6);
231     EXPECT_NE(word, (*hexwords).end());
232     EXPECT_EQ(std::get<0>(word->second), "PS_NUM");
233 
234     word = (*hexwords).find(7);
235     EXPECT_NE(word, (*hexwords).end());
236     EXPECT_EQ(std::get<0>(word->second), "VOLTAGE");
237 
238     auto& sid = entry->src.symptomID;
239     EXPECT_TRUE(sid);
240     EXPECT_EQ((*sid).size(), 3);
241     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
242     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
243     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
244 
245     EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
246     EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
247     auto& hexwordSource = entry->doc.messageArgSources;
248     EXPECT_TRUE(hexwordSource);
249     EXPECT_EQ((*hexwordSource).size(), 1);
250     EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
251 
252     const auto& jc = entry->journalCapture;
253     ASSERT_TRUE(jc);
254     ASSERT_TRUE(std::holds_alternative<size_t>(*jc));
255     EXPECT_EQ(std::get<size_t>(*jc), 7);
256 
257     entry = registry.lookup("0x2333", LookupType::reasonCode);
258     ASSERT_TRUE(entry);
259     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
260 }
261 
262 // Check the entry that mostly uses defaults
263 TEST_F(RegistryTest, TestFindEntryMinimal)
264 {
265     auto path = RegistryTest::writeData(registryData);
266     Registry registry{path};
267 
268     auto entry = registry.lookup("xyz.openbmc_project.Power.Fault",
269                                  LookupType::name);
270     ASSERT_TRUE(entry);
271     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
272     EXPECT_EQ(entry->subsystem, 0x61);
273     EXPECT_FALSE(entry->severity);
274     EXPECT_FALSE(entry->mfgSeverity);
275     EXPECT_FALSE(entry->mfgActionFlags);
276     EXPECT_FALSE(entry->actionFlags);
277     EXPECT_EQ(entry->componentID, 0x2000);
278     EXPECT_FALSE(entry->eventType);
279     EXPECT_FALSE(entry->eventScope);
280 
281     EXPECT_EQ(entry->src.reasonCode, 0x2030);
282     EXPECT_EQ(entry->src.type, 0xBD);
283     EXPECT_FALSE(entry->src.hexwordADFields);
284     EXPECT_FALSE(entry->src.symptomID);
285 }
286 
287 TEST_F(RegistryTest, TestBadJSON)
288 {
289     auto path = RegistryTest::writeData("bad {} json");
290 
291     Registry registry{path};
292 
293     EXPECT_FALSE(registry.lookup("foo", LookupType::name));
294 }
295 
296 // Test the helper functions the use the pel_values data.
297 TEST_F(RegistryTest, TestHelperFunctions)
298 {
299     using namespace openpower::pels::message::helper;
300     EXPECT_EQ(getSubsystem("input_power_source"), 0xA1);
301     EXPECT_THROW(getSubsystem("foo"), std::runtime_error);
302 
303     EXPECT_EQ(getSeverity("symptom_recovered"), 0x71);
304     EXPECT_THROW(getSeverity("foo"), std::runtime_error);
305 
306     EXPECT_EQ(getEventType("dump_notification"), 0x08);
307     EXPECT_THROW(getEventType("foo"), std::runtime_error);
308 
309     EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04);
310     EXPECT_THROW(getEventScope("foo"), std::runtime_error);
311 
312     std::vector<std::string> flags{"service_action", "dont_report",
313                                    "termination"};
314     EXPECT_EQ(getActionFlags(flags), 0x9100);
315 
316     flags.clear();
317     flags.push_back("foo");
318     EXPECT_THROW(getActionFlags(flags), std::runtime_error);
319 }
320 
321 TEST_F(RegistryTest, TestGetSRCReasonCode)
322 {
323     using namespace openpower::pels::message::helper;
324     EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
325               0x5555);
326 
327     EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
328                  std::runtime_error);
329 }
330 
331 TEST_F(RegistryTest, TestGetSRCType)
332 {
333     using namespace openpower::pels::message::helper;
334     EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
335     EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
336 
337     EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
338                  std::runtime_error);
339 
340     EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
341                  std::runtime_error);
342 }
343 
344 TEST_F(RegistryTest, TestGetSRCHexwordFields)
345 {
346     using namespace openpower::pels::message::helper;
347     const auto hexwords = R"(
348     {"Words6To9":
349       {
350         "8":
351         {
352             "Description": "TEST",
353             "AdditionalDataPropSource": "TEST"
354         }
355       }
356     })"_json;
357 
358     auto fields = getSRCHexwordFields(hexwords, "foo");
359     EXPECT_TRUE(fields);
360     auto word = fields->find(8);
361     EXPECT_NE(word, fields->end());
362 
363     const auto theInvalidRWord = R"(
364     {"Words6To9":
365       {
366         "R":
367         {
368             "Description": "TEST",
369             "AdditionalDataPropSource": "TEST"
370         }
371       }
372     })"_json;
373 
374     EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
375                  std::runtime_error);
376 }
377 
378 TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
379 {
380     using namespace openpower::pels::message::helper;
381     const auto sID = R"(
382     {
383         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
384     })"_json;
385 
386     auto fields = getSRCSymptomIDFields(sID, "foo");
387     EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
388     EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
389     EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
390 
391     const auto badField = R"(
392     {
393         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
394     })"_json;
395 
396     EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
397 }
398 
399 TEST_F(RegistryTest, TestGetComponentID)
400 {
401     using namespace openpower::pels::message::helper;
402 
403     // Get it from the JSON
404     auto id = getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json,
405                              "foo");
406     EXPECT_EQ(id, 0x4200);
407 
408     // Get it from the reason code on a 0xBD SRC
409     id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
410     EXPECT_EQ(id, 0x6700);
411 
412     // Not present on a 0x11 SRC
413     EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
414                  std::runtime_error);
415 }
416 
417 // Test when callouts are in the JSON.
418 TEST_F(RegistryTest, TestGetCallouts)
419 {
420     std::vector<std::string> names;
421 
422     {
423         // Callouts without AD, that depend on system type,
424         // where there is a default entry without a system type.
425         auto json = R"(
426         [
427         {
428             "System": "system1",
429             "CalloutList":
430             [
431                 {
432                     "Priority": "high",
433                     "LocCode": "P1-C1"
434                 },
435                 {
436                     "Priority": "low",
437                     "LocCode": "P1"
438                 },
439                 {
440                     "Priority": "low",
441                     "SymbolicFRU": "service_docs"
442                 },
443                 {
444                     "Priority": "low",
445                     "SymbolicFRUTrusted": "air_mover",
446                     "UseInventoryLocCode": true
447                 }
448             ]
449         },
450         {
451             "CalloutList":
452             [
453                 {
454                     "Priority": "medium",
455                     "Procedure": "bmc_code"
456                 },
457                 {
458                     "Priority": "low",
459                     "LocCode": "P3-C8",
460                     "SymbolicFRUTrusted": "service_docs"
461                 }
462             ]
463 
464         }
465         ])"_json;
466 
467         AdditionalData ad;
468         names.push_back("system1");
469 
470         auto callouts = Registry::getCallouts(json, names, ad);
471         EXPECT_EQ(callouts.size(), 4);
472         EXPECT_EQ(callouts[0].priority, "high");
473         EXPECT_EQ(callouts[0].locCode, "P1-C1");
474         EXPECT_EQ(callouts[0].procedure, "");
475         EXPECT_EQ(callouts[0].symbolicFRU, "");
476         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
477         EXPECT_EQ(callouts[1].priority, "low");
478         EXPECT_EQ(callouts[1].locCode, "P1");
479         EXPECT_EQ(callouts[1].procedure, "");
480         EXPECT_EQ(callouts[1].symbolicFRU, "");
481         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
482         EXPECT_EQ(callouts[2].priority, "low");
483         EXPECT_EQ(callouts[2].locCode, "");
484         EXPECT_EQ(callouts[2].procedure, "");
485         EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
486         EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
487         EXPECT_EQ(callouts[3].priority, "low");
488         EXPECT_EQ(callouts[3].locCode, "");
489         EXPECT_EQ(callouts[3].procedure, "");
490         EXPECT_EQ(callouts[3].symbolicFRU, "");
491         EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
492         EXPECT_EQ(callouts[3].useInventoryLocCode, true);
493 
494         // system2 isn't in the JSON, so it will pick the default one
495         names[0] = "system2";
496         callouts = Registry::getCallouts(json, names, ad);
497         EXPECT_EQ(callouts.size(), 2);
498         EXPECT_EQ(callouts[0].priority, "medium");
499         EXPECT_EQ(callouts[0].locCode, "");
500         EXPECT_EQ(callouts[0].procedure, "bmc_code");
501         EXPECT_EQ(callouts[0].symbolicFRU, "");
502         EXPECT_EQ(callouts[1].priority, "low");
503         EXPECT_EQ(callouts[1].locCode, "P3-C8");
504         EXPECT_EQ(callouts[1].procedure, "");
505         EXPECT_EQ(callouts[1].symbolicFRU, "");
506         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
507         EXPECT_EQ(callouts[1].useInventoryLocCode, false);
508     }
509 
510     // Empty JSON array (treated as an error)
511     {
512         auto json = R"([])"_json;
513         AdditionalData ad;
514         names[0] = "system1";
515         EXPECT_THROW(Registry::getCallouts(json, names, ad),
516                      std::runtime_error);
517     }
518 
519     {
520         // Callouts without AD, that depend on system type,
521         // where there isn't a default entry without a system type.
522         auto json = R"(
523         [
524         {
525             "System": "system1",
526             "CalloutList":
527             [
528                 {
529                     "Priority": "high",
530                     "LocCode": "P1-C1"
531                 },
532                 {
533                     "Priority": "low",
534                     "LocCode": "P1",
535                     "SymbolicFRU": "1234567"
536                 }
537             ]
538         },
539         {
540             "System": "system2",
541             "CalloutList":
542             [
543                 {
544                     "Priority": "medium",
545                     "LocCode": "P7",
546                     "CalloutType": "tool_fru"
547                 }
548             ]
549 
550         }
551         ])"_json;
552 
553         AdditionalData ad;
554         names[0] = "system1";
555 
556         auto callouts = Registry::getCallouts(json, names, ad);
557         EXPECT_EQ(callouts.size(), 2);
558         EXPECT_EQ(callouts[0].priority, "high");
559         EXPECT_EQ(callouts[0].locCode, "P1-C1");
560         EXPECT_EQ(callouts[0].procedure, "");
561         EXPECT_EQ(callouts[0].symbolicFRU, "");
562         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
563         EXPECT_EQ(callouts[1].priority, "low");
564         EXPECT_EQ(callouts[1].locCode, "P1");
565         EXPECT_EQ(callouts[1].procedure, "");
566         EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
567         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
568 
569         names[0] = "system2";
570         callouts = Registry::getCallouts(json, names, ad);
571         EXPECT_EQ(callouts.size(), 1);
572         EXPECT_EQ(callouts[0].priority, "medium");
573         EXPECT_EQ(callouts[0].locCode, "P7");
574         EXPECT_EQ(callouts[0].procedure, "");
575         EXPECT_EQ(callouts[0].symbolicFRU, "");
576         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
577 
578         // There is no entry for system3 or a default system,
579         // so this should fail.
580         names[0] = "system3";
581         EXPECT_THROW(Registry::getCallouts(json, names, ad),
582                      std::runtime_error);
583     }
584 
585     {
586         // Callouts that use the AdditionalData key PROC_NUM
587         // as an index into them, along with a system type.
588         // It supports PROC_NUMs 0 and 1.
589         auto json = R"(
590         {
591             "ADName": "PROC_NUM",
592             "CalloutsWithTheirADValues":
593             [
594                 {
595                     "ADValue": "0",
596                     "Callouts":
597                     [
598                         {
599                             "System": "system3",
600                             "CalloutList":
601                             [
602                                 {
603                                     "Priority": "high",
604                                     "LocCode": "P1-C5"
605                                 },
606                                 {
607                                     "Priority": "medium",
608                                     "LocCode": "P1-C6",
609                                     "SymbolicFRU": "1234567"
610                                 },
611                                 {
612                                     "Priority": "low",
613                                     "Procedure": "bmc_code",
614                                     "CalloutType": "config_procedure"
615                                 }
616                             ]
617                         },
618                         {
619                             "CalloutList":
620                             [
621                                 {
622                                     "Priority": "low",
623                                     "LocCode": "P55"
624                                 }
625                             ]
626                         }
627                     ]
628                 },
629                 {
630                     "ADValue": "1",
631                     "Callouts":
632                     [
633                         {
634                             "CalloutList":
635                             [
636                                 {
637                                     "Priority": "high",
638                                     "LocCode": "P1-C6",
639                                     "CalloutType": "external_fru"
640                                 }
641                             ]
642                         }
643                     ]
644                 }
645             ]
646         })"_json;
647 
648         {
649             // Find callouts for PROC_NUM 0 on system3
650             std::vector<std::string> adData{"PROC_NUM=0"};
651             AdditionalData ad{adData};
652             names[0] = "system3";
653 
654             auto callouts = Registry::getCallouts(json, names, ad);
655             EXPECT_EQ(callouts.size(), 3);
656             EXPECT_EQ(callouts[0].priority, "high");
657             EXPECT_EQ(callouts[0].locCode, "P1-C5");
658             EXPECT_EQ(callouts[0].procedure, "");
659             EXPECT_EQ(callouts[0].symbolicFRU, "");
660             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
661             EXPECT_EQ(callouts[1].priority, "medium");
662             EXPECT_EQ(callouts[1].locCode, "P1-C6");
663             EXPECT_EQ(callouts[1].procedure, "");
664             EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
665             EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
666             EXPECT_EQ(callouts[2].priority, "low");
667             EXPECT_EQ(callouts[2].locCode, "");
668             EXPECT_EQ(callouts[2].procedure, "bmc_code");
669             EXPECT_EQ(callouts[2].symbolicFRU, "");
670             EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
671 
672             // Find callouts for PROC_NUM 0 that uses the default system entry.
673             names[0] = "system99";
674 
675             callouts = Registry::getCallouts(json, names, ad);
676             EXPECT_EQ(callouts.size(), 1);
677             EXPECT_EQ(callouts[0].priority, "low");
678             EXPECT_EQ(callouts[0].locCode, "P55");
679             EXPECT_EQ(callouts[0].procedure, "");
680             EXPECT_EQ(callouts[0].symbolicFRU, "");
681             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
682         }
683         {
684             // Find callouts for PROC_NUM 1 that uses a default system entry.
685             std::vector<std::string> adData{"PROC_NUM=1"};
686             AdditionalData ad{adData};
687             names[0] = "system1";
688 
689             auto callouts = Registry::getCallouts(json, names, ad);
690             EXPECT_EQ(callouts.size(), 1);
691             EXPECT_EQ(callouts[0].priority, "high");
692             EXPECT_EQ(callouts[0].locCode, "P1-C6");
693             EXPECT_EQ(callouts[0].procedure, "");
694             EXPECT_EQ(callouts[0].symbolicFRU, "");
695             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
696         }
697         {
698             // There is no entry for PROC_NUM 2, so no callouts
699             std::vector<std::string> adData{"PROC_NUM=2"};
700             AdditionalData ad{adData};
701 
702             auto callouts = Registry::getCallouts(json, names, ad);
703             EXPECT_TRUE(callouts.empty());
704         }
705     }
706 
707     {
708         // Callouts with a 'CalloutsWhenNoADMatch' section that will
709         // be used when the AdditionalData value doesn't match.
710         auto json = R"(
711         {
712             "ADName": "PROC_NUM",
713             "CalloutsWithTheirADValues":
714             [
715                 {
716                     "ADValue": "0",
717                     "Callouts":
718                     [
719                         {
720                             "CalloutList":
721                             [
722                                 {
723                                     "Priority": "high",
724                                     "LocCode": "P0-C0"
725                                 }
726                             ]
727                         }
728                     ]
729                 }
730             ],
731             "CalloutsWhenNoADMatch": [
732                 {
733                     "CalloutList": [
734                         {
735                             "Priority": "medium",
736                             "LocCode": "P1-C1"
737                         }
738                     ]
739                 }
740             ]
741         })"_json;
742 
743         // There isn't an entry in the JSON for a PROC_NUM of 8
744         // so it should choose the P1-C1 callout.
745         std::vector<std::string> adData{"PROC_NUM=8"};
746         AdditionalData ad{adData};
747         names.clear();
748 
749         auto callouts = Registry::getCallouts(json, names, ad);
750         EXPECT_EQ(callouts.size(), 1);
751         EXPECT_EQ(callouts[0].priority, "medium");
752         EXPECT_EQ(callouts[0].locCode, "P1-C1");
753     }
754 }
755 
756 TEST_F(RegistryTest, TestNoSubsystem)
757 {
758     auto path = RegistryTest::writeData(registryData);
759     Registry registry{path};
760 
761     auto entry = registry.lookup("xyz.openbmc_project.Common.Error.Timeout",
762                                  LookupType::name);
763     ASSERT_TRUE(entry);
764     EXPECT_FALSE(entry->subsystem);
765 }
766 
767 TEST_F(RegistryTest, TestJournalSectionCapture)
768 {
769     auto path = RegistryTest::writeData(registryData);
770     Registry registry{path};
771 
772     auto entry = registry.lookup("xyz.openbmc_project.Journal.Capture",
773                                  LookupType::name);
774     ASSERT_TRUE(entry);
775 
776     const auto& jc = entry->journalCapture;
777     ASSERT_TRUE(jc);
778     ASSERT_TRUE(std::holds_alternative<AppCaptureList>(*jc));
779     const auto& acl = std::get<AppCaptureList>(*jc);
780 
781     ASSERT_EQ(acl.size(), 2);
782 
783     EXPECT_EQ(acl[0].syslogID, "test1");
784     EXPECT_EQ(acl[0].numLines, 5);
785 
786     EXPECT_EQ(acl[1].syslogID, "test2");
787     EXPECT_EQ(acl[1].numLines, 6);
788 }
789