Use less c++
As a design, it would be better if we didn't need to have a dependency
on c++ in a c library. Moving things to C will reduce both
dependencies and compile times.
Reduced dependencies makes the library itself easier to use in
more environments.
libmctp Is a library that doesn't take a dependency on gtest for its
tests
libpldm Is an example that does take a dependency on gtest.
Change-Id: If1dc638f87b75b28181a0baf67f5a18d389cff81
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/cli-app/cper-convert.c b/cli-app/cper-convert.c
index 082e452..942d258 100644
--- a/cli-app/cper-convert.c
+++ b/cli-app/cper-convert.c
@@ -15,7 +15,7 @@
#include <libcper/json-schema.h>
void cper_to_json(char *in_file, char *out_file, int is_single_section);
-void json_to_cper(char *in_file, char *out_file);
+void json_to_cper(const char *in_file, const char *out_file);
void print_help(void);
int main(int argc, char *argv[])
@@ -128,7 +128,7 @@
}
//Command for converting a provided CPER-JSON JSON file to CPER binary.
-void json_to_cper(char *in_file, char *out_file)
+void json_to_cper(const char *in_file, const char *out_file)
{
//Verify output file exists.
if (out_file == NULL) {
diff --git a/include/libcper/log.h b/include/libcper/log.h
index ccfca59..445d4d2 100644
--- a/include/libcper/log.h
+++ b/include/libcper/log.h
@@ -3,9 +3,17 @@
#ifndef LIBCPER_LOG_H
#define LIBCPER_LOG_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void cper_set_log_stdio();
void cper_set_log_custom(void (*fn)(const char *, ...));
void cper_print_log(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+#ifdef __cplusplus
+}
+#endif
+
#endif /* LIBCPER_LOG_H */
diff --git a/meson.build b/meson.build
index e669bb8..96e194c 100644
--- a/meson.build
+++ b/meson.build
@@ -21,6 +21,8 @@
add_project_arguments('-DLIBCPER_EXAMPLES="'
+ meson.current_source_dir() + '/examples"', language: ['c', 'cpp'])
+add_project_arguments('-Wno-unused-function', language: ['c', 'cpp'])
+
library_is_share = get_option('default_library') == 'shared'
add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c')
@@ -74,11 +76,9 @@
cc = meson.get_compiler('c')
json_c_dep = dependency('json-c')
-
libcper_include = ['include']
libcper_include_dir = include_directories(libcper_include, is_system: true)
-
libcper_parse_sources += files(
'base64.c',
'common-utils.c',
diff --git a/tests/base64_test.cpp b/tests/base64_test.cpp
index 6040c00..058de81 100644
--- a/tests/base64_test.cpp
+++ b/tests/base64_test.cpp
@@ -9,15 +9,15 @@
std::array<uint8_t, 1> data = { 'f' };
char *encoded = base64_encode(data.data(), data.size(), &encoded_len);
EXPECT_EQ(encoded_len, 4);
- ASSERT_EQ(std::string_view(encoded, encoded_len), "Zg==");
+ ASSERT_TRUE(memcmp(encoded, "Zg==", encoded_len) == 0);
free(encoded);
}
TEST(Base64Decode, Good)
{
int32_t decoded_len = 0;
- std::string_view data{ "Zg==" };
- UINT8 *decoded = base64_decode(data.data(), data.size(), &decoded_len);
+ const char *data{ "Zg==" };
+ UINT8 *decoded = base64_decode(data, strlen(data), &decoded_len);
EXPECT_EQ(decoded_len, 1);
ASSERT_EQ(decoded[0], 'f');
free(decoded);
diff --git a/tests/fuzz_cper_buf_to_ir.cpp b/tests/fuzz_cper_buf_to_ir.c
similarity index 74%
rename from tests/fuzz_cper_buf_to_ir.cpp
rename to tests/fuzz_cper_buf_to_ir.c
index b947782..a9ff504 100644
--- a/tests/fuzz_cper_buf_to_ir.cpp
+++ b/tests/fuzz_cper_buf_to_ir.c
@@ -1,8 +1,8 @@
-#include <cassert>
+#include <assert.h>
#include "libcper/cper-parse.h"
-#include "test-utils.hpp"
+#include "test-utils.h"
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
json_object *ir = cper_buf_to_ir(data, size);
if (ir == NULL) {
diff --git a/tests/ir-tests.cpp b/tests/ir-tests.cpp
index b30e2ec..37ee830 100644
--- a/tests/ir-tests.cpp
+++ b/tests/ir-tests.cpp
@@ -5,12 +5,7 @@
**/
#include <gtest/gtest.h>
-#include "test-utils.hpp"
-#include <cctype>
-#include <charconv>
-#include <filesystem>
-#include <format>
-#include <fstream>
+#include "test-utils.h"
#include <json.h>
#include <libcper/cper-parse.h>
#include <libcper/generator/cper-generate.h>
@@ -27,80 +22,172 @@
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(file_info *info)
+{
+ if (info == NULL) {
+ return;
+ }
+ free(info->cper_out);
+ free(info->json_out);
+ free(info);
+}
+
+file_info *file_info_init(const char *section_name)
+{
+ file_info *info = NULL;
+ char *buf = NULL;
+ size_t size;
+ int ret;
+
+ info = (file_info *)calloc(1, sizeof(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.
- fs::path file_path = LIBCPER_EXAMPLES;
- file_path /= section_name;
- fs::path cper_out = file_path.replace_extension("cperhex");
- fs::path json_out = file_path.replace_extension("json");
-
- char *buf;
+ json_object *ir = NULL;
size_t size;
- FILE *record = generate_record_memstream(§ion_name, 1, &buf, &size,
- 0, fixedValidbitsSet);
-
- // Write example CPER to disk
- std::ofstream outFile(cper_out, std::ios::binary);
- if (!outFile.is_open()) {
- std::cerr << "Failed to create/open CPER output file: "
- << cper_out << std::endl;
- return;
+ size_t file_size;
+ FILE *outFile = NULL;
+ std::vector<unsigned char> file_data;
+ FILE *record = NULL;
+ char *buf = NULL;
+ file_info *info = file_info_init(section_name);
+ if (info == NULL) {
+ goto done;
}
- std::vector<unsigned char> file_data;
+ 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) {
+ std::cerr << "Failed to create/open CPER output file: "
+ << info->cper_out << std::endl;
+ goto done;
+ }
+
fseek(record, 0, SEEK_END);
- size_t file_size = ftell(record);
+ file_size = ftell(record);
rewind(record);
file_data.resize(file_size);
if (fread(file_data.data(), 1, file_data.size(), record) != file_size) {
std::cerr << "Failed to read CPER data from memstream."
<< std::endl;
- FAIL();
- return;
+ EXPECT_EQ(false, true);
+ fclose(outFile);
+ goto done;
}
for (size_t index = 0; index < file_data.size(); index++) {
- outFile << std::format("{:02x}", file_data[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) {
- outFile << "\n";
+ fwrite("\n", sizeof(char), 1, outFile);
}
}
- outFile.close();
+ fclose(outFile);
//Convert to IR, free resources.
rewind(record);
- json_object *ir = cper_to_ir(record);
+ ir = cper_to_ir(record);
if (ir == NULL) {
std::cerr << "Empty JSON from CPER bin" << std::endl;
- FAIL();
- return;
+ EXPECT_EQ(false, true);
+ goto done;
}
//Write json output to disk
- json_object_to_file_ext(json_out.c_str(), ir, JSON_C_TO_STRING_PRETTY);
+ json_object_to_file_ext(info->json_out, ir, JSON_C_TO_STRING_PRETTY);
json_object_put(ir);
- fclose(record);
+done:
+ free_file_info(info);
+ if (record != NULL) {
+ fclose(record);
+ }
+ if (outFile != NULL) {
+ fclose(outFile);
+ }
free(buf);
}
-std::vector<unsigned char> string_to_binary(const std::string &source)
+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;
+}
+
+std::vector<unsigned char> string_to_binary(const char *source, size_t length)
{
std::vector<unsigned char> retval;
bool uppernibble = true;
- for (const char c : source) {
- unsigned char val = 0;
+ for (size_t i = 0; i < length; i++) {
+ char c = source[i];
if (c == '\n') {
continue;
}
- std::from_chars_result r = std::from_chars(&c, &c + 1, val, 16);
- EXPECT_TRUE(r.ec == std::error_code())
- << "Invalid hex character in test file: " << c;
+ int val = hex2int(c);
+ if (val < 0) {
+ printf("Invalid hex character in test file: %c\n", c);
+ return {};
+ }
if (uppernibble) {
- retval.push_back(val << 4);
+ retval.push_back((unsigned char)(val << 4));
} else {
- retval.back() += val;
+ retval.back() += (unsigned char)val;
}
uppernibble = !uppernibble;
}
@@ -111,32 +198,52 @@
void cper_example_section_ir_test(const char *section_name)
{
//Open CPER record for the given type.
- fs::path fpath = LIBCPER_EXAMPLES;
- fpath /= section_name;
- fs::path cper = fpath.replace_extension("cperhex");
- fs::path json = fpath.replace_extension("json");
+ file_info *info = file_info_init(section_name);
+ if (info == NULL) {
+ return;
+ }
- std::ifstream cper_file(cper, std::ios::binary);
- if (!cper_file.is_open()) {
- std::cerr << "Failed to open CPER file: " << cper << std::endl;
+ FILE *cper_file = fopen(info->cper_out, "rb");
+ if (cper_file == NULL) {
+ std::cerr << "Failed to open CPER file: " << info->cper_out
+ << std::endl;
+ free_file_info(info);
FAIL() << "Failed to open CPER file";
return;
}
- std::string cper_str((std::istreambuf_iterator<char>(cper_file)),
- std::istreambuf_iterator<char>());
+ 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) {
+ std::cerr << "Failed to read CPER file: " << info->cper_out
+ << std::endl;
+ free(buffer);
+ free_file_info(info);
+ return;
+ }
+ fclose(cper_file);
- std::vector<unsigned char> cper_bin = string_to_binary(cper_str);
+ std::vector<unsigned char> cper_bin = string_to_binary(buffer, length);
//Convert to IR, free resources.
json_object *ir = cper_buf_to_ir(cper_bin.data(), cper_bin.size());
if (ir == NULL) {
std::cerr << "Empty JSON from CPER bin" << std::endl;
+ free(buffer);
+ free_file_info(info);
FAIL();
return;
}
- json_object *expected = json_object_from_file(json.c_str());
- ASSERT_NE(expected, nullptr);
+ json_object *expected = json_object_from_file(info->json_out);
+ EXPECT_NE(expected, nullptr);
if (expected == nullptr) {
+ free(buffer);
+ free_file_info(info);
const char *str = json_object_to_json_string(ir);
const char *expected_str = json_object_to_json_string(expected);
@@ -146,9 +253,10 @@
}
EXPECT_TRUE(json_object_equal(ir, expected));
-
+ free(buffer);
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.
@@ -176,18 +284,32 @@
int valid = schema_validate_from_file(ir, single_section,
/*all_valid_bits*/ 1);
json_object_put(ir);
- EXPECT_TRUE(valid)
+ EXPECT_GE(valid, 0)
<< "IR validation test failed (single section mode = "
<< single_section << ")\n";
}
-std::string to_hex(char *input, size_t size)
+int to_hex(const unsigned char *input, size_t size, char **out)
{
- std::string out;
- for (char c : std::span<unsigned char>((unsigned char *)input, size)) {
- out += std::format("{:02x}", static_cast<unsigned char>(c));
+ *out = (char *)malloc(size * 2);
+ if (out == NULL) {
+ return -1;
}
- return out;
+ 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.
@@ -224,13 +346,23 @@
}
fclose(stream);
- std::cout << "size: " << size << ", cper_buf_size: " << cper_buf_size
- << std::endl;
- EXPECT_EQ(to_hex(buf, size),
- to_hex(cper_buf, std::min(size, cper_buf_size)))
- << "Binary output was not identical to input (single section mode = "
- << single_section << ").";
+ 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);
+
+ EXPECT_EQ(buf_hex_len, cper_buf_hex_len);
+ if (buf_hex_len == cper_buf_hex_len) {
+ EXPECT_EQ(memcmp(buf_hex, cper_buf_hex, buf_hex_len), 0)
+ << "Binary output was not identical to input (single section mode = "
+ << single_section << ").";
+ }
+
+ free(cper_buf_hex);
+ free(buf_hex);
//Free everything up.
fclose(record);
free(buf);
@@ -261,7 +393,7 @@
{
for (size_t i = 0; i < section_definitions_len; i++) {
//If a conversion one way exists, a conversion the other way must exist.
- std::string err =
+ const char *err =
"If a CPER conversion exists one way, there must be an equivalent method in reverse.";
if (section_definitions[i].ToCPER != NULL) {
ASSERT_NE(section_definitions[i].ToIR, nullptr) << err;
diff --git a/tests/meson.build b/tests/meson.build
index 2f1e2b2..0123dc2 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -9,7 +9,6 @@
dependencies: [
dependency('threads'),
gtest_proj.dependency('gtest'),
- gtest_proj.dependency('gtest_main'),
],
)
gmock = gtest_proj.dependency('gmock')
@@ -21,9 +20,12 @@
endif
endif
-jsonc_daccord = dependency('jsoncdac')
+jsonc_daccord = dependency(
+ 'jsoncdac',
+ default_options: ['default_library=static'],
+)
-test_sources = ['test-utils.cpp', 'base64_test.cpp']
+test_sources = ['test-utils.c', 'base64_test.cpp']
test_include_dirs = ['.', '../include']
@@ -33,7 +35,6 @@
test_sources,
implicit_include_directories: false,
include_directories: include_directories(test_include_dirs),
- cpp_args: '-fpermissive',
dependencies: [
json_c_dep,
jsonc_daccord,
@@ -43,7 +44,7 @@
gmock,
],
)
-test('test-cper-tests', cper_tests)
+test('test-cper-tests', cper_tests, protocol: 'gtest')
cxx = meson.get_compiler('cpp')
@@ -52,20 +53,20 @@
if (cxx.get_id() == 'clang') and get_option('fuzz').allowed() and not is_darwin
sanitize = ['fuzzer']
fuzz_args = [
- '-fsanitize=' + ','.join(sanitize),
'-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION',
+ '-fsanitize=fuzzer,address,leak',
]
foreach fuzzer_test : ['fuzz_cper_buf_to_ir']
fuzz_exe = executable(
fuzzer_test,
- [fuzzer_test + '.cpp'] + libcper_parse_sources + edk_sources + test_sources + libcper_generate_sources,
+ [fuzzer_test + '.c'] + libcper_parse_sources + edk_sources + 'test-utils.c' + libcper_generate_sources,
implicit_include_directories: false,
include_directories: include_directories(test_include_dirs),
cpp_args: fuzz_args,
c_args: fuzz_args,
link_args: fuzz_args,
- dependencies: [json_c_dep, jsonc_daccord, gtest, gmock],
+ dependencies: [json_c_dep, jsonc_daccord],
)
test(
fuzzer_test,
diff --git a/tests/test-utils.c b/tests/test-utils.c
new file mode 100644
index 0000000..1b2adac
--- /dev/null
+++ b/tests/test-utils.c
@@ -0,0 +1,248 @@
+/**
+ * Defines utility functions for testing CPER-JSON IR output from the cper-parse library.
+ *
+ * Author: Lawrence.Tang@arm.com
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "test-utils.h"
+
+#include <libcper/BaseTypes.h>
+#include <libcper/generator/cper-generate.h>
+
+#include <jsoncdaccord.h>
+#include <json.h>
+#include <libcper/log.h>
+
+// Objects that have mutually exclusive fields (and thereforce can't have both
+// required at the same time) can be added to this list.
+// Truly optional properties that shouldn't be added to "required" field for
+// validating the entire schema with validationbits=1
+// In most cases making sure examples set all valid bits is preferable to adding to this list
+static const char *optional_props[] = {
+ // Some sections don't parse header correctly?
+ "header",
+
+ // Each section is optional
+ "GenericProcessor", "Ia32x64Processor", "ArmProcessor", "Memory",
+ "Memory2", "Pcie", "PciBus", "PciComponent", "Firmware", "GenericDmar",
+ "VtdDmar", "IommuDmar", "CcixPer", "CxlProtocol", "CxlComponent",
+ "Nvidia", "Ampere", "Unknown",
+
+ // CXL? might have a bug?
+ "partitionID",
+
+ // CXL protocol
+ "capabilityStructure", "deviceSerial",
+
+ // CXL component
+ "cxlComponentEventLog", "addressSpace", "errorType",
+ "participationType", "timedOut", "level", "operation", "preciseIP",
+ "restartableIP", "overflow", "uncorrected", "transactionType",
+
+ // PCIe AER
+ "addressSpace", "errorType", "participationType", "timedOut", "level",
+ "operation", "preciseIP", "restartableIP", "overflow", "uncorrected",
+ "transactionType"
+};
+
+//Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
+FILE *generate_record_memstream(const char **types, UINT16 num_types,
+ char **buf, size_t *buf_size,
+ int single_section,
+ GEN_VALID_BITS_TEST_TYPE validBitsType)
+{
+ //Open a memory stream.
+ FILE *stream = open_memstream(buf, buf_size);
+
+ //Generate a section to the stream, close & return.
+ if (!single_section) {
+ generate_cper_record((char **)(types), num_types, stream,
+ validBitsType);
+ } else {
+ generate_single_section_record((char *)(types[0]), stream,
+ validBitsType);
+ }
+ fclose(stream);
+
+ //Return fmemopen() buffer for reading.
+ return fmemopen(*buf, *buf_size, "r");
+}
+
+int iterate_make_required_props(json_object *jsonSchema, int all_valid_bits)
+{
+ //properties
+ json_object *properties =
+ json_object_object_get(jsonSchema, "properties");
+
+ if (properties != NULL) {
+ json_object *requrired_arr = json_object_new_array();
+
+ json_object_object_foreach(properties, property_name,
+ property_value)
+ {
+ (void)property_value;
+ int add_to_required = 1;
+ size_t num = sizeof(optional_props) /
+ sizeof(optional_props[0]);
+ for (size_t i = 0; i < num; i++) {
+ if (strcmp(optional_props[i], property_name) ==
+ 0) {
+ add_to_required = 0;
+ break;
+ }
+ }
+
+ if (add_to_required) {
+ //Add to list if property is not optional
+ json_object_array_add(
+ requrired_arr,
+ json_object_new_string(property_name));
+ }
+ }
+
+ json_object_object_foreach(properties, property_name2,
+ property_value2)
+ {
+ (void)property_name2;
+ if (iterate_make_required_props(property_value2,
+ all_valid_bits) < 0) {
+ json_object_put(requrired_arr);
+ return -1;
+ }
+ }
+
+ if (all_valid_bits) {
+ json_object_object_add(jsonSchema, "required",
+ requrired_arr);
+ } else {
+ json_object_put(requrired_arr);
+ }
+ }
+
+ // ref
+ json_object *ref = json_object_object_get(jsonSchema, "$ref");
+ if (ref != NULL) {
+ const char *ref_str = json_object_get_string(ref);
+ if (ref_str != NULL) {
+ if (strlen(ref_str) < 1) {
+ cper_print_log("Failed seek filepath: %s\n",
+ ref_str);
+ return -1;
+ }
+ size_t size =
+ strlen(LIBCPER_JSON_SPEC) + strlen(ref_str);
+ char *path = (char *)malloc(size);
+ int n = snprintf(path, size, "%s%s", LIBCPER_JSON_SPEC,
+ ref_str + 1);
+ if (n != (int)size - 1) {
+ cper_print_log("Failed concat filepath: %s\n",
+ ref_str);
+ free(path);
+ return -1;
+ }
+ json_object *ref_obj = json_object_from_file(path);
+ free(path);
+ if (ref_obj == NULL) {
+ cper_print_log("Failed to parse file: %s\n",
+ ref_str);
+ return -1;
+ }
+
+ if (iterate_make_required_props(ref_obj,
+ all_valid_bits) < 0) {
+ json_object_put(ref_obj);
+ return -1;
+ }
+
+ json_object_object_foreach(ref_obj, key, val)
+ {
+ json_object_object_add(jsonSchema, key,
+ json_object_get(val));
+ }
+ json_object_object_del(jsonSchema, "$ref");
+
+ json_object_put(ref_obj);
+ }
+ }
+
+ //oneOf
+ const json_object *oneOf = json_object_object_get(jsonSchema, "oneOf");
+ if (oneOf != NULL) {
+ size_t num_elements = json_object_array_length(oneOf);
+
+ for (size_t i = 0; i < num_elements; i++) {
+ json_object *obj = json_object_array_get_idx(oneOf, i);
+ if (iterate_make_required_props(obj, all_valid_bits) <
+ 0) {
+ return -1;
+ }
+ }
+ }
+
+ //items
+ const json_object *items = json_object_object_get(jsonSchema, "items");
+ if (items != NULL) {
+ json_object_object_foreach(items, key, val)
+ {
+ (void)key;
+ if (iterate_make_required_props(val, all_valid_bits) <
+ 0) {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int schema_validate_from_file(json_object *to_test, int single_section,
+ int all_valid_bits)
+{
+ const char *schema_file;
+ if (single_section) {
+ schema_file = "cper-json-section-log.json";
+ } else {
+ schema_file = "cper-json-full-log.json";
+ }
+ int size = strlen(schema_file) + 1 + strlen(LIBCPER_JSON_SPEC) + 1;
+ char *schema_path = malloc(size);
+ snprintf(schema_path, size, "%s/%s", LIBCPER_JSON_SPEC, schema_file);
+
+ json_object *schema = json_object_from_file(schema_path);
+
+ if (schema == NULL) {
+ cper_print_log("Could not parse schema file: %s", schema_path);
+ free(schema_path);
+ return 0;
+ }
+
+ if (iterate_make_required_props(schema, all_valid_bits) < 0) {
+ cper_print_log("Failed to make required props\n");
+ json_object_put(schema);
+ free(schema_path);
+ return -1;
+ }
+
+ int err = jdac_validate(to_test, schema);
+ if (err == JDAC_ERR_VALID) {
+ cper_print_log("validation ok\n");
+ json_object_put(schema);
+ free(schema_path);
+ return 1;
+ }
+
+ cper_print_log("validate failed %d: %s\n", err, jdac_errorstr(err));
+
+ cper_print_log("schema: \n%s\n",
+ json_object_to_json_string_ext(schema,
+ JSON_C_TO_STRING_PRETTY));
+ cper_print_log("to_test: \n%s\n",
+ json_object_to_json_string_ext(to_test,
+ JSON_C_TO_STRING_PRETTY));
+ json_object_put(schema);
+ free(schema_path);
+ return 0;
+}
diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp
deleted file mode 100644
index e4d79d3..0000000
--- a/tests/test-utils.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * Defines utility functions for testing CPER-JSON IR output from the cper-parse library.
- *
- * Author: Lawrence.Tang@arm.com
- **/
-
-#include <cstdio>
-#include <cstdlib>
-#include <fstream>
-#include <map>
-#include <filesystem>
-#include <vector>
-#include <algorithm>
-#include "test-utils.hpp"
-
-#include <libcper/BaseTypes.h>
-#include <libcper/generator/cper-generate.h>
-
-extern "C" {
-#include <jsoncdaccord.h>
-#include <json.h>
-#include <libcper/log.h>
-}
-
-namespace fs = std::filesystem;
-
-// Objects that have mutually exclusive fields (and thereforce can't have both
-// required at the same time) can be added to this list.
-// Truly optional properties that shouldn't be added to "required" field for
-// validating the entire schema with validationbits=1
-// In most cases making sure examples set all valid bits is preferable to adding to this list
-const static std::vector<std::string> optional_props = {
- { // Some sections don't parse header correctly?
- "header",
-
- // Each section is optional
- "GenericProcessor", "Ia32x64Processor", "ArmProcessor", "Memory",
- "Memory2", "Pcie", "PciBus", "PciComponent", "Firmware",
- "GenericDmar", "VtdDmar", "IommuDmar", "CcixPer", "CxlProtocol",
- "CxlComponent", "Nvidia", "Ampere", "Unknown",
-
- // CXL? might have a bug?
- "partitionID",
-
- // CXL protocol
- "capabilityStructure", "deviceSerial",
-
- // CXL component
- "cxlComponentEventLog", "addressSpace", "errorType",
- "participationType", "timedOut", "level", "operation", "preciseIP",
- "restartableIP", "overflow", "uncorrected", "transactionType",
-
- // PCIe AER
- "addressSpace", "errorType", "participationType", "timedOut", "level",
- "operation", "preciseIP", "restartableIP", "overflow", "uncorrected",
- "transactionType" }
-};
-
-//Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
-FILE *generate_record_memstream(const char **types, UINT16 num_types,
- char **buf, size_t *buf_size,
- int single_section,
- GEN_VALID_BITS_TEST_TYPE validBitsType)
-{
- //Open a memory stream.
- FILE *stream = open_memstream(buf, buf_size);
-
- //Generate a section to the stream, close & return.
- if (!single_section) {
- generate_cper_record(const_cast<char **>(types), num_types,
- stream, validBitsType);
- } else {
- generate_single_section_record(const_cast<char *>(types[0]),
- stream, validBitsType);
- }
- fclose(stream);
-
- //Return fmemopen() buffer for reading.
- return fmemopen(*buf, *buf_size, "r");
-}
-
-int iterate_make_required_props(json_object *jsonSchema, bool all_valid_bits)
-{
- //properties
- json_object *properties =
- json_object_object_get(jsonSchema, "properties");
-
- if (properties != nullptr) {
- json_object *requrired_arr = json_object_new_array();
-
- json_object_object_foreach(properties, property_name,
- property_value)
- {
- bool add_to_required = true;
- const auto it_find_opt_prop = std::ranges::find(
- optional_props, property_name);
- if (it_find_opt_prop != optional_props.end()) {
- add_to_required = false;
- }
-
- if (add_to_required) {
- //Add to list if property is not optional
- json_object_array_add(
- requrired_arr,
- json_object_new_string(property_name));
- }
- }
-
- json_object_object_foreach(properties, property_name2,
- property_value2)
- {
- (void)property_name2;
- if (iterate_make_required_props(property_value2,
- all_valid_bits) < 0) {
- return -1;
- }
- }
-
- if (all_valid_bits) {
- json_object_object_add(jsonSchema, "required",
- requrired_arr);
- }
- //json_object_put(requrired_arr);
- }
-
- // ref
- json_object *ref = json_object_object_get(jsonSchema, "$ref");
- if (ref != nullptr) {
- const char *ref_str = json_object_get_string(ref);
- if (ref_str != nullptr) {
- std::string ref_path = LIBCPER_JSON_SPEC;
- // remove the leading .
- ref_path += std::string(ref_str).substr(1);
- json_object *ref_obj =
- json_object_from_file(ref_path.c_str());
- if (ref_obj == nullptr) {
- printf("Failed to parse file: %s\n",
- ref_path.c_str());
- return -1;
- }
-
- if (iterate_make_required_props(ref_obj,
- all_valid_bits) < 0) {
- json_object_put(ref_obj);
- return -1;
- }
-
- json_object_object_foreach(ref_obj, key, val)
- {
- json_object_object_add(jsonSchema, key,
- json_object_get(val));
- }
- json_object_object_del(jsonSchema, "$ref");
-
- json_object_put(ref_obj);
- }
- }
-
- //oneOf
- const json_object *oneOf = json_object_object_get(jsonSchema, "oneOf");
- if (oneOf != nullptr) {
- size_t num_elements = json_object_array_length(oneOf);
-
- for (size_t i = 0; i < num_elements; i++) {
- json_object *obj = json_object_array_get_idx(oneOf, i);
- if (iterate_make_required_props(obj, all_valid_bits) <
- 0) {
- return -1;
- }
- }
- }
-
- //items
- const json_object *items = json_object_object_get(jsonSchema, "items");
- if (items != nullptr) {
- json_object_object_foreach(items, key, val)
- {
- (void)key;
- if (iterate_make_required_props(val, all_valid_bits) <
- 0) {
- return -1;
- }
- }
- }
-
- return 1;
-}
-
-int schema_validate_from_file(json_object *to_test, int single_section,
- int all_valid_bits)
-{
- const char *schema_file;
- if (single_section) {
- schema_file = "cper-json-section-log.json";
- } else {
- schema_file = "cper-json-full-log.json";
- }
-
- std::string schema_path = LIBCPER_JSON_SPEC;
- schema_path += "/";
- schema_path += schema_file;
-
- json_object *schema = json_object_from_file(schema_path.c_str());
- if (schema == nullptr) {
- cper_print_log("Could not parse schema file: %s", schema_file);
- return 0;
- }
-
- if (iterate_make_required_props(schema, all_valid_bits) < 0) {
- printf("Failed to make required props\n");
- json_object_put(schema);
- return -1;
- }
-
- int err = jdac_validate(to_test, schema);
- if (err == JDAC_ERR_VALID) {
- printf("validation ok\n");
- json_object_put(schema);
- return 1;
- }
- printf("validate failed %d: %s\n", err, jdac_errorstr(err));
-
- printf("schema: \n%s\n",
- json_object_to_json_string_ext(schema, JSON_C_TO_STRING_PRETTY));
- printf("to_test: \n%s\n", json_object_to_json_string_ext(
- to_test, JSON_C_TO_STRING_PRETTY));
- json_object_put(schema);
- return 0;
-}
diff --git a/tests/test-utils.hpp b/tests/test-utils.h
similarity index 84%
rename from tests/test-utils.hpp
rename to tests/test-utils.h
index 3b1d061..a7fb723 100644
--- a/tests/test-utils.hpp
+++ b/tests/test-utils.h
@@ -1,6 +1,10 @@
#ifndef CPER_IR_TEST_UTILS_H
#define CPER_IR_TEST_UTILS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdio.h>
#include <libcper/BaseTypes.h>
#include <libcper/generator/sections/gen-section.h>
@@ -9,7 +13,7 @@
// Controls whether required properties are added to the majority of property
// definitions. This is useful for unit tests that are validating JSON where
// all fields are valid
-enum class AddRequiredProps { YES, NO };
+enum AddRequiredProps { AddRequired, NoModify };
FILE *generate_record_memstream(const char **types, UINT16 num_types,
char **buf, size_t *buf_size,
@@ -19,4 +23,8 @@
int schema_validate_from_file(json_object *to_test, int single_section,
int all_valid_bits);
+#ifdef __cplusplus
+}
+#endif
+
#endif