blob: 88f9d7d5de316274ed6e0e0af124a646e501935d [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
18// Truly optional properties that shouldn't be added to "required" field for
19// validating the entire schema with validationbits=1
20const static std::map<std::string, std::vector<std::string> >
21 optional_properties_map = {
22 { "./sections/cper-arm-processor.json",
23 { "vendorSpecificInfo" } },
24 { "./cper-json-section-log.json", { "header" } },
25 { "./sections/cper-cxl-protocol.json",
26 { "capabilityStructure", "deviceSerial" } },
27 { "./sections/cper-generic-dmar.json",
28 { "faultReason", "description" } },
29 { "./sections/cper-cxl-component.json",
30 { "cxlComponentEventLog" } },
31 };
32
33nlohmann::json loadJson(const char *filePath)
34{
35 std::ifstream file(filePath);
36 if (!file.is_open()) {
37 std::cerr << "Failed to open file: " << filePath << std::endl;
38 }
39 nlohmann::json out = nlohmann::json::parse(file, nullptr, false);
40 return out;
41}
42
Lawrence Tangd34f2b12022-07-19 15:36:31 +010043//Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
Lawrence Tange407b4c2022-07-21 13:54:01 +010044FILE *generate_record_memstream(const char **types, UINT16 num_types,
John Chungf8fc7052024-05-03 20:05:29 +080045 char **buf, size_t *buf_size,
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080046 int single_section,
47 GEN_VALID_BITS_TEST_TYPE validBitsType)
Lawrence Tangd34f2b12022-07-19 15:36:31 +010048{
Lawrence Tange407b4c2022-07-21 13:54:01 +010049 //Open a memory stream.
50 FILE *stream = open_memstream(buf, buf_size);
Lawrence Tangd34f2b12022-07-19 15:36:31 +010051
Lawrence Tange407b4c2022-07-21 13:54:01 +010052 //Generate a section to the stream, close & return.
John Chungf8fc7052024-05-03 20:05:29 +080053 if (!single_section) {
54 generate_cper_record(const_cast<char **>(types), num_types,
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080055 stream, validBitsType);
John Chungf8fc7052024-05-03 20:05:29 +080056 } else {
57 generate_single_section_record(const_cast<char *>(types[0]),
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080058 stream, validBitsType);
John Chungf8fc7052024-05-03 20:05:29 +080059 }
Lawrence Tange407b4c2022-07-21 13:54:01 +010060 fclose(stream);
Lawrence Tangd34f2b12022-07-19 15:36:31 +010061
Lawrence Tange407b4c2022-07-21 13:54:01 +010062 //Return fmemopen() buffer for reading.
63 return fmemopen(*buf, *buf_size, "r");
John Chungf8fc7052024-05-03 20:05:29 +080064}
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -080065
66void iterate_make_required_props(nlohmann::json &jsonSchema,
67 std::vector<std::string> &optional_props)
68{
69 //id
70 const auto it_id = jsonSchema.find("$id");
71 if (it_id != jsonSchema.end()) {
72 auto id_strptr = it_id->get_ptr<const std::string *>();
73 std::string id_str = *id_strptr;
74 if (id_str.find("header") != std::string::npos ||
75 id_str.find("section-descriptor") != std::string::npos) {
76 return;
77 }
78 }
79 //oneOf
80 const auto it_oneof = jsonSchema.find("oneOf");
81 if (it_oneof != jsonSchema.end()) {
82 //Iterate over oneOf properties
83 for (auto &oneOfProp : *it_oneof) {
84 iterate_make_required_props(oneOfProp, optional_props);
85 }
86 }
87
88 //items
89 const auto it_items = jsonSchema.find("items");
90 if (it_items != jsonSchema.end()) {
91 iterate_make_required_props(*it_items, optional_props);
92 }
93 //required
94 const auto it_req = jsonSchema.find("required");
95 if (it_req == jsonSchema.end()) {
96 return;
97 }
98
99 //properties
100 const auto it_prop = jsonSchema.find("properties");
101 if (it_prop == jsonSchema.end()) {
102 return;
103 }
104 nlohmann::json &propertyFields = *it_prop;
105 nlohmann::json::array_t property_list;
106 if (propertyFields.is_object()) {
107 for (auto &[key, value] : propertyFields.items()) {
108 const auto it_find_opt_prop =
109 std::find(optional_props.begin(),
110 optional_props.end(), key);
111 if (it_find_opt_prop == optional_props.end()) {
112 //Add to list if property is not optional
113 property_list.push_back(key);
114 }
115
116 iterate_make_required_props(value, optional_props);
117 }
118 }
119
120 *it_req = property_list;
121}
122
123// Document loader callback function
124const nlohmann::json *documentLoader(const std::string &uri)
125{
126 // Load the schema from a file
127 nlohmann::json *ref_schema = new nlohmann::json;
128 *ref_schema = loadJson(uri.c_str());
129 if (ref_schema->is_discarded()) {
130 std::cerr << "Could not open schema file: " << uri << std::endl;
131 }
132 std::vector<std::string> opt = {};
133 const auto it_optional_file = optional_properties_map.find(uri);
134 if (it_optional_file != optional_properties_map.end()) {
135 opt = it_optional_file->second;
136 }
137 iterate_make_required_props(*ref_schema, opt);
138
139 return ref_schema;
140}
141
142// Document release callback function
143void documentRelease(const nlohmann::json *adapter)
144{
145 delete adapter; // Free the adapter memory
146}
147
148int schema_validate_from_file(const char *schema_file_path,
149 nlohmann::json &jsonData,
150 std::string &error_message)
151{
152 // Load the schema
153 nlohmann::json schema_root = loadJson(schema_file_path);
154 if (schema_root.is_discarded()) {
155 std::cerr << "Could not open schema file: " << schema_file_path
156 << std::endl;
157 return 0;
158 }
159
160 fs::path pathObj(schema_file_path);
161 fs::path base_path = pathObj.parent_path();
162 try {
163 fs::current_path(base_path);
164 // std::cout << "Changed directory to: " << fs::current_path()
165 // << std::endl;
166 } catch (const fs::filesystem_error &e) {
167 std::cerr << "Filesystem error: " << e.what() << std::endl;
168 }
169
170 // Parse the json schema into an internal schema format
171 valijson::Schema schema;
172 valijson::SchemaParser parser;
173 valijson::adapters::NlohmannJsonAdapter schemaDocumentAdapter(
174 schema_root);
175
176 // Set up callbacks for resolving external references
177 try {
178 parser.populateSchema(schemaDocumentAdapter, schema,
179 documentLoader, documentRelease);
180 } catch (std::exception &e) {
181 std::cerr << "Failed to parse schema: " << e.what()
182 << std::endl;
183 return 0;
184 }
185
186 // Perform validation
187 valijson::Validator validator(valijson::Validator::kStrongTypes);
188 valijson::ValidationResults results;
189 valijson::adapters::NlohmannJsonAdapter targetDocumentAdapter(jsonData);
190 if (!validator.validate(schema, targetDocumentAdapter, &results)) {
191 std::cerr << "Validation failed." << std::endl;
192 valijson::ValidationResults::Error error;
193 unsigned int errorNum = 1;
194 while (results.popError(error)) {
195 std::string context;
Ed Tanousbadae112025-03-10 13:38:51 -0700196 for (const std::string &str : error.context) {
197 context += str;
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800198 }
199
Ed Tanousbadae112025-03-10 13:38:51 -0700200 std::cout << "Error #" << errorNum << '\n'
201 << " context: " << context << '\n'
202 << " desc: " << error.description << '\n';
Aushim Nagarkattiae8f6d92025-01-29 17:34:44 -0800203 ++errorNum;
204 }
205 return 0;
206 }
207
208 error_message = "Schema validation successful";
209 return 1;
210}