blob: 11d8a02de7b6639a35f6737186b98b8a5310f44c [file] [log] [blame]
Lawrence Tangd34f2b12022-07-19 15:36:31 +01001/**
2 * Defines utility functions for testing CPER-JSON IR output from the cper-parse library.
Ed Tanousfedd4572024-07-12 13:56:00 -07003 *
Lawrence Tangd34f2b12022-07-19 15:36:31 +01004 * Author: Lawrence.Tang@arm.com
5 **/
6
John Chungf8fc7052024-05-03 20:05:29 +08007#include <cstdio>
8#include <cstdlib>
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -08009#include <fstream>
10#include <filesystem>
Lawrence Tangd34f2b12022-07-19 15:36:31 +010011#include "test-utils.hpp"
Karthik Rajagopalan255bd812024-09-06 14:36:34 -070012
Thu Nguyene42fb482024-10-15 14:43:11 +000013#include <libcper/BaseTypes.h>
14#include <libcper/generator/cper-generate.h>
Lawrence Tangd34f2b12022-07-19 15:36:31 +010015
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080016namespace fs = std::filesystem;
17
Ed Tanous2c4d7b62025-03-16 12:22:02 -070018// Objects that have mutually exclusive fields (and thereforce can't have both
19// required at the same time) can be added to this list.
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080020// Truly optional properties that shouldn't be added to "required" field for
21// validating the entire schema with validationbits=1
22const static std::map<std::string, std::vector<std::string> >
23 optional_properties_map = {
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080024 { "./sections/cper-cxl-protocol.json",
25 { "capabilityStructure", "deviceSerial" } },
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080026 { "./sections/cper-cxl-component.json",
27 { "cxlComponentEventLog" } },
Ed Tanousd6b62632025-03-14 15:30:07 -070028 { "./sections/cper-ia32x64-processor.json",
29 { "addressSpace", "errorType", "participationType",
30 "timedOut", "level", "operation", "preciseIP",
31 "restartableIP", "overflow", "uncorrected",
32 "transactionType" } },
Erwin Tsaur8870c072025-02-28 12:57:12 -080033 // Several PCIe properties are dependent on the device capability
34 // version and whether the device is running in Flit-Mode.
35 // All of these properties are optional.
36 { "./sections/cper-pcie.json",
37 { "device_capabilities2", "device_status2", "device_control2",
38 "link_capabilities2", "link_status2", "link_control2",
39 "slot_capabilities2", "slot_status2", "slot_control2" } },
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080040 };
41
42nlohmann::json loadJson(const char *filePath)
43{
44 std::ifstream file(filePath);
45 if (!file.is_open()) {
46 std::cerr << "Failed to open file: " << filePath << std::endl;
47 }
48 nlohmann::json out = nlohmann::json::parse(file, nullptr, false);
49 return out;
50}
51
Lawrence Tangd34f2b12022-07-19 15:36:31 +010052//Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
Lawrence Tange407b4c2022-07-21 13:54:01 +010053FILE *generate_record_memstream(const char **types, UINT16 num_types,
John Chungf8fc7052024-05-03 20:05:29 +080054 char **buf, size_t *buf_size,
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080055 int single_section,
56 GEN_VALID_BITS_TEST_TYPE validBitsType)
Lawrence Tangd34f2b12022-07-19 15:36:31 +010057{
Lawrence Tange407b4c2022-07-21 13:54:01 +010058 //Open a memory stream.
59 FILE *stream = open_memstream(buf, buf_size);
Lawrence Tangd34f2b12022-07-19 15:36:31 +010060
Lawrence Tange407b4c2022-07-21 13:54:01 +010061 //Generate a section to the stream, close & return.
John Chungf8fc7052024-05-03 20:05:29 +080062 if (!single_section) {
63 generate_cper_record(const_cast<char **>(types), num_types,
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080064 stream, validBitsType);
John Chungf8fc7052024-05-03 20:05:29 +080065 } else {
66 generate_single_section_record(const_cast<char *>(types[0]),
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080067 stream, validBitsType);
John Chungf8fc7052024-05-03 20:05:29 +080068 }
Lawrence Tange407b4c2022-07-21 13:54:01 +010069 fclose(stream);
Lawrence Tangd34f2b12022-07-19 15:36:31 +010070
Lawrence Tange407b4c2022-07-21 13:54:01 +010071 //Return fmemopen() buffer for reading.
72 return fmemopen(*buf, *buf_size, "r");
John Chungf8fc7052024-05-03 20:05:29 +080073}
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080074
75void iterate_make_required_props(nlohmann::json &jsonSchema,
76 std::vector<std::string> &optional_props)
77{
78 //id
79 const auto it_id = jsonSchema.find("$id");
80 if (it_id != jsonSchema.end()) {
81 auto id_strptr = it_id->get_ptr<const std::string *>();
82 std::string id_str = *id_strptr;
83 if (id_str.find("header") != std::string::npos ||
84 id_str.find("section-descriptor") != std::string::npos) {
85 return;
86 }
87 }
88 //oneOf
89 const auto it_oneof = jsonSchema.find("oneOf");
90 if (it_oneof != jsonSchema.end()) {
91 //Iterate over oneOf properties
92 for (auto &oneOfProp : *it_oneof) {
93 iterate_make_required_props(oneOfProp, optional_props);
94 }
95 }
96
97 //items
98 const auto it_items = jsonSchema.find("items");
99 if (it_items != jsonSchema.end()) {
100 iterate_make_required_props(*it_items, optional_props);
101 }
102 //required
103 const auto it_req = jsonSchema.find("required");
104 if (it_req == jsonSchema.end()) {
105 return;
106 }
107
108 //properties
109 const auto it_prop = jsonSchema.find("properties");
110 if (it_prop == jsonSchema.end()) {
111 return;
112 }
113 nlohmann::json &propertyFields = *it_prop;
114 nlohmann::json::array_t property_list;
115 if (propertyFields.is_object()) {
116 for (auto &[key, value] : propertyFields.items()) {
117 const auto it_find_opt_prop =
118 std::find(optional_props.begin(),
119 optional_props.end(), key);
120 if (it_find_opt_prop == optional_props.end()) {
121 //Add to list if property is not optional
122 property_list.push_back(key);
123 }
124
125 iterate_make_required_props(value, optional_props);
126 }
127 }
128
129 *it_req = property_list;
130}
131
132// Document loader callback function
Ed Tanousd6b62632025-03-14 15:30:07 -0700133const nlohmann::json *documentLoader(const std::string &uri,
134 AddRequiredProps add_required_props)
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800135{
136 // Load the schema from a file
Ed Tanousd6b62632025-03-14 15:30:07 -0700137 std::unique_ptr<nlohmann::json> ref_schema =
138 std::make_unique<nlohmann::json>();
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800139 *ref_schema = loadJson(uri.c_str());
140 if (ref_schema->is_discarded()) {
141 std::cerr << "Could not open schema file: " << uri << std::endl;
142 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700143 if (add_required_props == AddRequiredProps::YES) {
144 std::vector<std::string> opt = {};
145 const auto it_optional_file = optional_properties_map.find(uri);
146 if (it_optional_file != optional_properties_map.end()) {
147 opt = it_optional_file->second;
148 }
149 iterate_make_required_props(*ref_schema, opt);
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800150 }
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800151
Ed Tanousd6b62632025-03-14 15:30:07 -0700152 return ref_schema.release();
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800153}
154
155// Document release callback function
156void documentRelease(const nlohmann::json *adapter)
157{
158 delete adapter; // Free the adapter memory
159}
160
Ed Tanousd6b62632025-03-14 15:30:07 -0700161std::unique_ptr<valijson::Schema>
162load_schema(AddRequiredProps add_required_props, int single_section)
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800163{
164 // Load the schema
Ed Tanousd6b62632025-03-14 15:30:07 -0700165 fs::path pathObj(LIBCPER_JSON_SPEC);
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800166
Ed Tanousd6b62632025-03-14 15:30:07 -0700167 if (single_section) {
168 pathObj /= "cper-json-section-log.json";
169 } else {
170 pathObj /= "cper-json-full-log.json";
171 }
172 nlohmann::json schema_root = loadJson(pathObj.c_str());
173 fs::path base_path(LIBCPER_JSON_SPEC);
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800174 try {
175 fs::current_path(base_path);
176 // std::cout << "Changed directory to: " << fs::current_path()
177 // << std::endl;
178 } catch (const fs::filesystem_error &e) {
179 std::cerr << "Filesystem error: " << e.what() << std::endl;
180 }
181
182 // Parse the json schema into an internal schema format
Ed Tanousd6b62632025-03-14 15:30:07 -0700183 std::unique_ptr<valijson::Schema> schema =
184 std::make_unique<valijson::Schema>();
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800185 valijson::SchemaParser parser;
186 valijson::adapters::NlohmannJsonAdapter schemaDocumentAdapter(
187 schema_root);
188
189 // Set up callbacks for resolving external references
190 try {
Ed Tanousd6b62632025-03-14 15:30:07 -0700191 parser.populateSchema(
192 schemaDocumentAdapter, *schema,
193 [add_required_props](const std::string &uri) {
194 return documentLoader(uri, add_required_props);
195 },
196 documentRelease);
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800197 } catch (std::exception &e) {
198 std::cerr << "Failed to parse schema: " << e.what()
199 << std::endl;
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800200 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700201 return schema;
202}
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800203
Ed Tanousd6b62632025-03-14 15:30:07 -0700204int schema_validate_from_file(const valijson::Schema &schema,
205 nlohmann::json &jsonData,
206 std::string &error_message)
207{
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800208 // Perform validation
209 valijson::Validator validator(valijson::Validator::kStrongTypes);
210 valijson::ValidationResults results;
211 valijson::adapters::NlohmannJsonAdapter targetDocumentAdapter(jsonData);
212 if (!validator.validate(schema, targetDocumentAdapter, &results)) {
213 std::cerr << "Validation failed." << std::endl;
214 valijson::ValidationResults::Error error;
215 unsigned int errorNum = 1;
216 while (results.popError(error)) {
217 std::string context;
Ed Tanousbadae112025-03-10 13:38:51 -0700218 for (const std::string &str : error.context) {
219 context += str;
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800220 }
221
Ed Tanousbadae112025-03-10 13:38:51 -0700222 std::cout << "Error #" << errorNum << '\n'
223 << " context: " << context << '\n'
224 << " desc: " << error.description << '\n';
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800225 ++errorNum;
226 }
227 return 0;
228 }
229
230 error_message = "Schema validation successful";
231 return 1;
232}