xref: /openbmc/libcper/tests/test-utils.c (revision edee0a37f42ce7c9444b3b470b0c5df407caf629)
1 /**
2  * Defines utility functions for testing CPER-JSON IR output from the cper-parse library.
3  *
4  * Author: Lawrence.Tang@arm.com
5  **/
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include "test-utils.h"
11 
12 #include <libcper/BaseTypes.h>
13 #include <libcper/generator/cper-generate.h>
14 
15 #include <jsoncdaccord.h>
16 #include <json.h>
17 #include <libcper/log.h>
18 
19 // Objects that have mutually exclusive fields (and thereforce can't have both
20 // required at the same time) can be added to this list.
21 // Truly optional properties that shouldn't be added to "required" field for
22 // validating the entire schema with validationbits=1
23 // In most cases making sure examples set all valid bits is preferable to adding to this list
24 static const char *optional_props[] = {
25 	// Some sections don't parse header correctly?
26 	"header",
27 
28 	// Each section is optional
29 	"GenericProcessor", "Ia32x64Processor", "ArmProcessor", "Memory",
30 	"Memory2", "Pcie", "PciBus", "PciComponent", "Firmware", "GenericDmar",
31 	"VtdDmar", "IommuDmar", "CcixPer", "CxlProtocol", "CxlComponent",
32 	"Nvidia", "Ampere", "Unknown",
33 
34 	// CXL?  might have a bug?
35 	"partitionID",
36 
37 	// CXL protocol
38 	"capabilityStructure", "deviceSerial",
39 
40 	// CXL component
41 	"cxlComponentEventLog", "addressSpace", "errorType",
42 	"participationType", "timedOut", "level", "operation", "preciseIP",
43 	"restartableIP", "overflow", "uncorrected", "transactionType",
44 
45 	// PCIe AER
46 	"addressSpace", "errorType", "participationType", "timedOut", "level",
47 	"operation", "preciseIP", "restartableIP", "overflow", "uncorrected",
48 	"transactionType"
49 };
50 
51 //Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
generate_record_memstream(const char ** types,UINT16 num_types,char ** buf,size_t * buf_size,int single_section,GEN_VALID_BITS_TEST_TYPE validBitsType)52 FILE *generate_record_memstream(const char **types, UINT16 num_types,
53 				char **buf, size_t *buf_size,
54 				int single_section,
55 				GEN_VALID_BITS_TEST_TYPE validBitsType)
56 {
57 	//Open a memory stream.
58 	FILE *stream = open_memstream(buf, buf_size);
59 
60 	//Generate a section to the stream, close & return.
61 	if (!single_section) {
62 		generate_cper_record((char **)(types), num_types, stream,
63 				     validBitsType);
64 	} else {
65 		generate_single_section_record((char *)(types[0]), stream,
66 					       validBitsType);
67 	}
68 	fclose(stream);
69 
70 	//Return fmemopen() buffer for reading.
71 	return fmemopen(*buf, *buf_size, "r");
72 }
73 
iterate_make_required_props(json_object * jsonSchema,int all_valid_bits)74 int iterate_make_required_props(json_object *jsonSchema, int all_valid_bits)
75 {
76 	//properties
77 	json_object *properties =
78 		json_object_object_get(jsonSchema, "properties");
79 
80 	if (properties != NULL) {
81 		json_object *requrired_arr = json_object_new_array();
82 
83 		json_object_object_foreach(properties, property_name,
84 					   property_value)
85 		{
86 			(void)property_value;
87 			int add_to_required = 1;
88 			size_t num = sizeof(optional_props) /
89 				     sizeof(optional_props[0]);
90 			for (size_t i = 0; i < num; i++) {
91 				if (strcmp(optional_props[i], property_name) ==
92 				    0) {
93 					add_to_required = 0;
94 					break;
95 				}
96 			}
97 
98 			if (add_to_required) {
99 				//Add to list if property is not optional
100 				json_object_array_add(
101 					requrired_arr,
102 					json_object_new_string(property_name));
103 			}
104 		}
105 
106 		json_object_object_foreach(properties, property_name2,
107 					   property_value2)
108 		{
109 			(void)property_name2;
110 			if (iterate_make_required_props(property_value2,
111 							all_valid_bits) < 0) {
112 				json_object_put(requrired_arr);
113 				return -1;
114 			}
115 		}
116 
117 		if (all_valid_bits) {
118 			json_object_object_add(jsonSchema, "required",
119 					       requrired_arr);
120 		} else {
121 			json_object_put(requrired_arr);
122 		}
123 	}
124 
125 	// ref
126 	json_object *ref = json_object_object_get(jsonSchema, "$ref");
127 	if (ref != NULL) {
128 		const char *ref_str = json_object_get_string(ref);
129 		if (ref_str != NULL) {
130 			if (strlen(ref_str) < 1) {
131 				cper_print_log("Failed seek filepath: %s\n",
132 					       ref_str);
133 				return -1;
134 			}
135 			size_t size =
136 				strlen(LIBCPER_JSON_SPEC) + strlen(ref_str);
137 			char *path = (char *)malloc(size);
138 			int n = snprintf(path, size, "%s%s", LIBCPER_JSON_SPEC,
139 					 ref_str + 1);
140 			if (n != (int)size - 1) {
141 				cper_print_log("Failed concat filepath: %s\n",
142 					       ref_str);
143 				free(path);
144 				return -1;
145 			}
146 			json_object *ref_obj = json_object_from_file(path);
147 			free(path);
148 			if (ref_obj == NULL) {
149 				cper_print_log("Failed to parse file: %s\n",
150 					       ref_str);
151 				return -1;
152 			}
153 
154 			if (iterate_make_required_props(ref_obj,
155 							all_valid_bits) < 0) {
156 				json_object_put(ref_obj);
157 				return -1;
158 			}
159 
160 			json_object_object_foreach(ref_obj, key, val)
161 			{
162 				json_object_object_add(jsonSchema, key,
163 						       json_object_get(val));
164 			}
165 			json_object_object_del(jsonSchema, "$ref");
166 
167 			json_object_put(ref_obj);
168 		}
169 	}
170 
171 	//oneOf
172 	const json_object *oneOf = json_object_object_get(jsonSchema, "oneOf");
173 	if (oneOf != NULL) {
174 		size_t num_elements = json_object_array_length(oneOf);
175 
176 		for (size_t i = 0; i < num_elements; i++) {
177 			json_object *obj = json_object_array_get_idx(oneOf, i);
178 			if (iterate_make_required_props(obj, all_valid_bits) <
179 			    0) {
180 				return -1;
181 			}
182 		}
183 	}
184 
185 	//items
186 	const json_object *items = json_object_object_get(jsonSchema, "items");
187 	if (items != NULL) {
188 		json_object_object_foreach(items, key, val)
189 		{
190 			(void)key;
191 			if (iterate_make_required_props(val, all_valid_bits) <
192 			    0) {
193 				return -1;
194 			}
195 		}
196 	}
197 
198 	return 1;
199 }
200 
schema_validate_from_file(json_object * to_test,int single_section,int all_valid_bits)201 int schema_validate_from_file(json_object *to_test, int single_section,
202 			      int all_valid_bits)
203 {
204 	const char *schema_file;
205 	if (single_section) {
206 		schema_file = "cper-json-section-log.json";
207 	} else {
208 		schema_file = "cper-json-full-log.json";
209 	}
210 	int size = strlen(schema_file) + 1 + strlen(LIBCPER_JSON_SPEC) + 1;
211 	char *schema_path = malloc(size);
212 	snprintf(schema_path, size, "%s/%s", LIBCPER_JSON_SPEC, schema_file);
213 
214 	json_object *schema = json_object_from_file(schema_path);
215 
216 	if (schema == NULL) {
217 		cper_print_log("Could not parse schema file: %s", schema_path);
218 		free(schema_path);
219 		return 0;
220 	}
221 
222 	if (iterate_make_required_props(schema, all_valid_bits) < 0) {
223 		cper_print_log("Failed to make required props\n");
224 		json_object_put(schema);
225 		free(schema_path);
226 		return -1;
227 	}
228 
229 	int err = jdac_validate(to_test, schema);
230 	if (err == JDAC_ERR_VALID) {
231 		cper_print_log("validation ok\n");
232 		json_object_put(schema);
233 		free(schema_path);
234 		return 1;
235 	}
236 
237 	cper_print_log("validate failed %d: %s\n", err, jdac_errorstr(err));
238 
239 	cper_print_log("schema: \n%s\n",
240 		       json_object_to_json_string_ext(schema,
241 						      JSON_C_TO_STRING_PRETTY));
242 	cper_print_log("to_test: \n%s\n",
243 		       json_object_to_json_string_ext(to_test,
244 						      JSON_C_TO_STRING_PRETTY));
245 	json_object_put(schema);
246 	free(schema_path);
247 	return 0;
248 }
249