| /** |
| * Defines tests for validating CPER-JSON IR output from the cper-parse library. |
| * |
| * Author: Lawrence.Tang@arm.com |
| **/ |
| |
| #include "test-utils.h" |
| #include "string.h" |
| #include "assert.h" |
| #include <ctype.h> |
| #include <json.h> |
| #include <libcper/cper-parse.h> |
| #include <libcper/generator/cper-generate.h> |
| #include <libcper/generator/sections/gen-section.h> |
| #include <libcper/json-schema.h> |
| #include <libcper/sections/cper-section.h> |
| |
| #include "base64_test.h" |
| |
| /* |
| * Test templates. |
| */ |
| static const GEN_VALID_BITS_TEST_TYPE allValidbitsSet = ALL_VALID; |
| static const GEN_VALID_BITS_TEST_TYPE fixedValidbitsSet = SOME_VALID; |
| static const int GEN_EXAMPLES = 0; |
| |
| static const char *cper_ext = "cperhex"; |
| static const char *json_ext = "json"; |
| |
| struct file_info { |
| char *cper_out; |
| char *json_out; |
| }; |
| |
| void free_file_info(struct file_info *info) |
| { |
| if (info == NULL) { |
| return; |
| } |
| free(info->cper_out); |
| free(info->json_out); |
| free(info); |
| } |
| |
| struct file_info *file_info_init(const char *section_name) |
| { |
| struct file_info *info = NULL; |
| char *buf = NULL; |
| size_t size; |
| int ret; |
| |
| info = (struct file_info *)calloc(1, sizeof(struct file_info)); |
| if (info == NULL) { |
| goto fail; |
| } |
| |
| size = strlen(LIBCPER_EXAMPLES) + 1 + strlen(section_name) + 1 + |
| strlen(cper_ext) + 1; |
| info->cper_out = (char *)malloc(size); |
| ret = snprintf(info->cper_out, size, "%s/%s.%s", LIBCPER_EXAMPLES, |
| section_name, cper_ext); |
| if (ret != (int)size - 1) { |
| printf("snprintf0 failed\n"); |
| goto fail; |
| } |
| size = strlen(LIBCPER_EXAMPLES) + 1 + strlen(section_name) + 1 + |
| strlen(json_ext) + 1; |
| info->json_out = (char *)malloc(size); |
| ret = snprintf(info->json_out, size, "%s/%s.%s", LIBCPER_EXAMPLES, |
| section_name, json_ext); |
| if (ret != (int)size - 1) { |
| printf("snprintf3 failed\n"); |
| goto fail; |
| } |
| free(buf); |
| return info; |
| |
| fail: |
| free(buf); |
| free_file_info(info); |
| return NULL; |
| } |
| |
| void cper_create_examples(const char *section_name) |
| { |
| //Generate full CPER record for the given type. |
| json_object *ir = NULL; |
| size_t size; |
| size_t file_size; |
| FILE *outFile = NULL; |
| unsigned char *file_data; |
| FILE *record = NULL; |
| char *buf = NULL; |
| struct file_info *info = file_info_init(section_name); |
| if (info == NULL) { |
| goto done; |
| } |
| |
| record = generate_record_memstream(§ion_name, 1, &buf, &size, 0, |
| fixedValidbitsSet); |
| |
| // Write example CPER to disk |
| outFile = fopen(info->cper_out, "wb"); |
| if (outFile == NULL) { |
| printf("Failed to create/open CPER output file: %s\n", |
| info->cper_out); |
| goto done; |
| } |
| |
| fseek(record, 0, SEEK_END); |
| file_size = ftell(record); |
| rewind(record); |
| file_data = malloc(file_size); |
| if (fread(file_data, 1, file_size, record) != file_size) { |
| printf("Failed to read CPER data from memstream."); |
| fclose(outFile); |
| assert(0); |
| |
| goto done; |
| } |
| for (size_t index = 0; index < file_size; index++) { |
| char hex_str[3]; |
| int out = snprintf(hex_str, sizeof(hex_str), "%02x", |
| file_data[index]); |
| if (out != 2) { |
| printf("snprintf1 failed\n"); |
| goto done; |
| } |
| fwrite(hex_str, sizeof(char), 2, outFile); |
| if (index % 30 == 29) { |
| fwrite("\n", sizeof(char), 1, outFile); |
| } |
| } |
| fclose(outFile); |
| |
| //Convert to IR, free resources. |
| rewind(record); |
| ir = cper_to_ir(record); |
| if (ir == NULL) { |
| printf("Empty JSON from CPER bin2\n"); |
| assert(0); |
| goto done; |
| } |
| |
| //Write json output to disk |
| json_object_to_file_ext(info->json_out, ir, JSON_C_TO_STRING_PRETTY); |
| json_object_put(ir); |
| |
| done: |
| free_file_info(info); |
| if (record != NULL) { |
| fclose(record); |
| } |
| if (outFile != NULL) { |
| fclose(outFile); |
| } |
| free(buf); |
| } |
| |
| int hex2int(char ch) |
| { |
| if ((ch >= '0') && (ch <= '9')) { |
| return ch - '0'; |
| } |
| if ((ch >= 'A') && (ch <= 'F')) { |
| return ch - 'A' + 10; |
| } |
| if ((ch >= 'a') && (ch <= 'f')) { |
| return ch - 'a' + 10; |
| } |
| return -1; |
| } |
| |
| int string_to_binary(const char *source, size_t length, unsigned char **retval) |
| { |
| size_t retval_size = length * 2; |
| *retval = malloc(retval_size); |
| int uppernibble = 1; |
| |
| size_t ret_index = 0; |
| |
| for (size_t i = 0; i < length; i++) { |
| char c = source[i]; |
| if (c == '\n') { |
| continue; |
| } |
| int val = hex2int(c); |
| if (val < 0) { |
| printf("Invalid hex character in test file: %c\n", c); |
| return -1; |
| } |
| |
| if (uppernibble) { |
| (*retval)[ret_index] = (unsigned char)(val << 4); |
| } else { |
| (*retval)[ret_index] += (unsigned char)val; |
| ret_index++; |
| } |
| uppernibble = !uppernibble; |
| } |
| return ret_index; |
| } |
| |
| //Tests fixed CPER sections for IR validity with an example set. |
| void cper_example_section_ir_test(const char *section_name) |
| { |
| //Open CPER record for the given type. |
| struct file_info *info = file_info_init(section_name); |
| if (info == NULL) { |
| return; |
| } |
| |
| FILE *cper_file = fopen(info->cper_out, "rb"); |
| if (cper_file == NULL) { |
| printf("Failed to open CPER file: %s\n", info->cper_out); |
| free_file_info(info); |
| assert(0); |
| return; |
| } |
| fseek(cper_file, 0, SEEK_END); |
| size_t length = ftell(cper_file); |
| fseek(cper_file, 0, SEEK_SET); |
| char *buffer = (char *)malloc(length); |
| if (!buffer) { |
| free_file_info(info); |
| return; |
| } |
| if (fread(buffer, 1, length, cper_file) != length) { |
| printf("Failed to read CPER file: %s\n", info->cper_out); |
| free(buffer); |
| free_file_info(info); |
| return; |
| } |
| fclose(cper_file); |
| |
| unsigned char *cper_bin; |
| int cper_bin_len = string_to_binary(buffer, length, &cper_bin); |
| if (cper_bin_len <= 0) { |
| free(buffer); |
| free_file_info(info); |
| assert(0); |
| return; |
| } |
| printf("cper_bin: %s\n", cper_bin); |
| printf("cper_bin_len: %d\n", cper_bin_len); |
| |
| //Convert to IR, free resources. |
| json_object *ir = cper_buf_to_ir(cper_bin, cper_bin_len); |
| if (ir == NULL) { |
| printf("Empty JSON from CPER bin3\n"); |
| free(cper_bin); |
| free(buffer); |
| free_file_info(info); |
| assert(0); |
| return; |
| } |
| |
| json_object *expected = json_object_from_file(info->json_out); |
| assert(expected != NULL); |
| if (expected == NULL) { |
| free(buffer); |
| free(cper_bin); |
| free_file_info(info); |
| const char *str = json_object_to_json_string(ir); |
| |
| const char *expected_str = json_object_to_json_string(expected); |
| assert(strcmp(str, expected_str) == 0); |
| return; |
| } |
| |
| assert(json_object_equal(ir, expected)); |
| free(buffer); |
| free(cper_bin); |
| json_object_put(ir); |
| json_object_put(expected); |
| free_file_info(info); |
| } |
| |
| //Tests a single randomly generated CPER section of the given type to ensure CPER-JSON IR validity. |
| void cper_log_section_ir_test(const char *section_name, int single_section, |
| GEN_VALID_BITS_TEST_TYPE validBitsType) |
| { |
| //Generate full CPER record for the given type. |
| char *buf; |
| size_t size; |
| FILE *record = generate_record_memstream(§ion_name, 1, &buf, &size, |
| single_section, validBitsType); |
| |
| //Convert to IR, free resources. |
| json_object *ir; |
| if (single_section) { |
| ir = cper_single_section_to_ir(record); |
| } else { |
| ir = cper_to_ir(record); |
| } |
| |
| fclose(record); |
| free(buf); |
| |
| //Validate against schema. |
| int valid = schema_validate_from_file(ir, single_section, |
| /*all_valid_bits*/ 1); |
| json_object_put(ir); |
| |
| if (valid < 0) { |
| printf("IR validation test failed (single section mode = %d)\n", |
| single_section); |
| assert(0); |
| } |
| } |
| |
| int to_hex(const unsigned char *input, size_t size, char **out) |
| { |
| *out = (char *)malloc(size * 2); |
| if (out == NULL) { |
| return -1; |
| } |
| int out_index = 0; |
| for (size_t i = 0; i < size; i++) { |
| unsigned char c = input[i]; |
| char hex_str[3]; |
| int n = snprintf(hex_str, sizeof(hex_str), "%02x", c); |
| if (n != 2) { |
| printf("snprintf2 failed with code %d\n", n); |
| return -1; |
| } |
| (*out)[out_index] = hex_str[0]; |
| out_index++; |
| (*out)[out_index] = hex_str[1]; |
| out_index++; |
| } |
| return out_index; |
| } |
| |
| //Checks for binary round-trip equality for a given randomly generated CPER record. |
| void cper_log_section_binary_test(const char *section_name, int single_section, |
| GEN_VALID_BITS_TEST_TYPE validBitsType) |
| { |
| //Generate CPER record for the given type. |
| char *buf; |
| size_t size; |
| FILE *record = generate_record_memstream(§ion_name, 1, &buf, &size, |
| single_section, validBitsType); |
| if (record == NULL) { |
| printf("Could not generate memstream for binary test"); |
| return; |
| } |
| |
| //Convert to IR. |
| json_object *ir; |
| if (single_section) { |
| ir = cper_single_section_to_ir(record); |
| } else { |
| ir = cper_to_ir(record); |
| } |
| |
| //Now convert back to binary, and get a stream out. |
| char *cper_buf; |
| size_t cper_buf_size; |
| FILE *stream = open_memstream(&cper_buf, &cper_buf_size); |
| if (single_section) { |
| ir_single_section_to_cper(ir, stream); |
| } else { |
| ir_to_cper(ir, stream); |
| } |
| fclose(stream); |
| |
| printf("size: %zu, cper_buf_size: %zu\n", size, cper_buf_size); |
| |
| char *buf_hex; |
| int buf_hex_len = to_hex((unsigned char *)buf, size, &buf_hex); |
| char *cper_buf_hex; |
| int cper_buf_hex_len = |
| to_hex((unsigned char *)cper_buf, cper_buf_size, &cper_buf_hex); |
| |
| printf("%.*s\n", cper_buf_hex_len, cper_buf_hex); |
| printf("%.*s\n", buf_hex_len, buf_hex); |
| assert(buf_hex_len == cper_buf_hex_len); |
| assert(memcmp(buf_hex, cper_buf_hex, buf_hex_len) == 0); |
| |
| free(buf_hex); |
| free(cper_buf_hex); |
| |
| //Free everything up. |
| fclose(record); |
| free(buf); |
| free(cper_buf); |
| json_object_put(ir); |
| } |
| |
| //Tests randomly generated CPER sections for IR validity of a given type, in both single section mode and full CPER log mode. |
| void cper_log_section_dual_ir_test(const char *section_name) |
| { |
| cper_log_section_ir_test(section_name, 0, allValidbitsSet); |
| cper_log_section_ir_test(section_name, 1, allValidbitsSet); |
| //Validate against examples |
| cper_example_section_ir_test(section_name); |
| } |
| |
| //Tests randomly generated CPER sections for binary compatibility of a given type, in both single section mode and full CPER log mode. |
| void cper_log_section_dual_binary_test(const char *section_name) |
| { |
| cper_log_section_binary_test(section_name, 0, allValidbitsSet); |
| cper_log_section_binary_test(section_name, 1, allValidbitsSet); |
| } |
| |
| /* |
| * Non-single section assertions. |
| */ |
| void CompileTimeAssertions_TwoWayConversion() |
| { |
| for (size_t i = 0; i < section_definitions_len; i++) { |
| //If a conversion one way exists, a conversion the other way must exist. |
| if (section_definitions[i].ToCPER != NULL) { |
| assert(section_definitions[i].ToIR != NULL); |
| } |
| if (section_definitions[i].ToIR != NULL) { |
| assert(section_definitions[i].ToCPER != NULL); |
| } |
| } |
| } |
| |
| void CompileTimeAssertions_ShortcodeNoSpaces() |
| { |
| for (size_t i = 0; i < generator_definitions_len; i++) { |
| for (int j = 0; |
| generator_definitions[i].ShortName[j + 1] != '\0'; j++) { |
| assert(isspace(generator_definitions[i].ShortName[j]) == |
| 0); |
| } |
| } |
| } |
| |
| /* |
| * Single section tests. |
| */ |
| |
| //Generic processor tests. |
| void GenericProcessorTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("generic"); |
| } |
| void GenericProcessorTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("generic"); |
| } |
| |
| //IA32/x64 tests. |
| void IA32x64Tests_IRValid() |
| { |
| cper_log_section_dual_ir_test("ia32x64"); |
| } |
| void IA32x64Tests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("ia32x64"); |
| } |
| |
| // void IPFTests_IRValid() { |
| // cper_log_section_dual_ir_test("ipf"); |
| // } |
| |
| //ARM tests. |
| void ArmTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("arm"); |
| } |
| void ArmTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("arm"); |
| } |
| |
| //Memory tests. |
| void MemoryTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("memory"); |
| } |
| void MemoryTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("memory"); |
| } |
| |
| //Memory 2 tests. |
| void Memory2Tests_IRValid() |
| { |
| cper_log_section_dual_ir_test("memory2"); |
| } |
| void Memory2Tests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("memory2"); |
| } |
| |
| //PCIe tests. |
| void PCIeTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("pcie"); |
| } |
| void PCIeTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("pcie"); |
| } |
| |
| //Firmware tests. |
| void FirmwareTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("firmware"); |
| } |
| void FirmwareTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("firmware"); |
| } |
| |
| //PCI Bus tests. |
| void PCIBusTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("pcibus"); |
| } |
| void PCIBusTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("pcibus"); |
| } |
| |
| //PCI Device tests. |
| void PCIDevTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("pcidev"); |
| } |
| void PCIDevTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("pcidev"); |
| } |
| |
| //Generic DMAr tests. |
| void DMArGenericTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("dmargeneric"); |
| } |
| void DMArGenericTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("dmargeneric"); |
| } |
| |
| //VT-d DMAr tests. |
| void DMArVtdTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("dmarvtd"); |
| } |
| void DMArVtdTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("dmarvtd"); |
| } |
| |
| //IOMMU DMAr tests. |
| void DMArIOMMUTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("dmariommu"); |
| } |
| void DMArIOMMUTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("dmariommu"); |
| } |
| |
| //CCIX PER tests. |
| void CCIXPERTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("ccixper"); |
| } |
| void CCIXPERTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("ccixper"); |
| } |
| |
| //CXL Protocol tests. |
| void CXLProtocolTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("cxlprotocol"); |
| } |
| void CXLProtocolTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("cxlprotocol"); |
| } |
| |
| //CXL Component tests. |
| void CXLComponentTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("cxlcomponent-media"); |
| } |
| void CXLComponentTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("cxlcomponent-media"); |
| } |
| |
| //NVIDIA section tests. |
| void NVIDIASectionTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("nvidia"); |
| } |
| void NVIDIASectionTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("nvidia"); |
| } |
| |
| void NVIDIACMETSectionTests_IRValid() |
| { |
| cper_example_section_ir_test("nvidia_cmet_info"); |
| } |
| |
| //Unknown section tests. |
| void UnknownSectionTests_IRValid() |
| { |
| cper_log_section_dual_ir_test("unknown"); |
| } |
| void UnknownSectionTests_BinaryEqual() |
| { |
| cper_log_section_dual_binary_test("unknown"); |
| } |
| |
| //Entrypoint for the testing program. |
| int main() |
| { |
| if (GEN_EXAMPLES) { |
| cper_create_examples("arm"); |
| cper_create_examples("ia32x64"); |
| cper_create_examples("memory"); |
| cper_create_examples("memory2"); |
| cper_create_examples("pcie"); |
| cper_create_examples("firmware"); |
| cper_create_examples("pcibus"); |
| cper_create_examples("pcidev"); |
| cper_create_examples("dmargeneric"); |
| cper_create_examples("dmarvtd"); |
| cper_create_examples("dmariommu"); |
| cper_create_examples("ccixper"); |
| cper_create_examples("cxlprotocol"); |
| cper_create_examples("cxlcomponent-media"); |
| cper_create_examples("nvidia"); |
| cper_create_examples("unknown"); |
| } |
| test_base64_encode_good(); |
| test_base64_decode_good(); |
| GenericProcessorTests_IRValid(); |
| GenericProcessorTests_BinaryEqual(); |
| IA32x64Tests_IRValid(); |
| IA32x64Tests_BinaryEqual(); |
| ArmTests_IRValid(); |
| ArmTests_BinaryEqual(); |
| MemoryTests_IRValid(); |
| MemoryTests_BinaryEqual(); |
| Memory2Tests_IRValid(); |
| Memory2Tests_BinaryEqual(); |
| PCIeTests_IRValid(); |
| PCIeTests_BinaryEqual(); |
| FirmwareTests_IRValid(); |
| FirmwareTests_BinaryEqual(); |
| PCIBusTests_IRValid(); |
| PCIBusTests_BinaryEqual(); |
| PCIDevTests_IRValid(); |
| PCIDevTests_BinaryEqual(); |
| DMArGenericTests_IRValid(); |
| DMArGenericTests_BinaryEqual(); |
| DMArVtdTests_IRValid(); |
| DMArVtdTests_BinaryEqual(); |
| DMArIOMMUTests_IRValid(); |
| DMArIOMMUTests_BinaryEqual(); |
| CCIXPERTests_IRValid(); |
| CCIXPERTests_BinaryEqual(); |
| CXLProtocolTests_IRValid(); |
| CXLProtocolTests_BinaryEqual(); |
| CXLComponentTests_IRValid(); |
| CXLComponentTests_BinaryEqual(); |
| NVIDIASectionTests_IRValid(); |
| NVIDIASectionTests_BinaryEqual(); |
| NVIDIACMETSectionTests_IRValid(); |
| UnknownSectionTests_IRValid(); |
| UnknownSectionTests_BinaryEqual(); |
| CompileTimeAssertions_TwoWayConversion(); |
| CompileTimeAssertions_ShortcodeNoSpaces(); |
| |
| printf("\n\nTest completed successfully.\n"); |
| |
| return 0; |
| } |