// Copyright © 2024 Rot127 // SPDX-License-Identifier: BSD-3 #ifndef TESTCASE_H #define TESTCASE_H #include "test_detail.h" #include #include #include #include /// Input data for a test case. typedef struct { char *name; uint8_t *bytes; // mandatory uint32_t bytes_count; // Filled by cyaml char *arch; // mandatory uint64_t address; char **options; // mandatory uint32_t options_count; // Filled by cyaml } TestInput; TestInput *test_input_new(); void test_input_free(TestInput *test_input); TestInput *test_input_clone(TestInput *test_input); char *test_input_stringify(const TestInput *test_input, const char *postfix); cs_arch test_input_get_cs_arch(const TestInput *test_input); cs_mode test_input_get_cs_mode(const TestInput *test_input); void test_input_get_cs_option(const TestInput *test_input, cs_opt_type *otype, cs_opt_value *oval); /// A single byte static const cyaml_schema_value_t byte_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, uint8_t), }; /// A single option string static const cyaml_schema_value_t option_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const cyaml_schema_field_t test_input_mapping_schema[] = { CYAML_FIELD_STRING_PTR("name", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestInput, name, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("bytes", CYAML_FLAG_POINTER, TestInput, bytes, &byte_schema, 0, CYAML_UNLIMITED), // 0-MAX bytes CYAML_FIELD_STRING_PTR("arch", CYAML_FLAG_POINTER, TestInput, arch, 0, CYAML_UNLIMITED), CYAML_FIELD_UINT("address", CYAML_FLAG_SCALAR_PLAIN | CYAML_FLAG_OPTIONAL, TestInput, address), CYAML_FIELD_SEQUENCE("options", CYAML_FLAG_POINTER, TestInput, options, &option_schema, 0, CYAML_UNLIMITED), // 0-MAX options CYAML_FIELD_END }; /// Data compared to the produced cs_insn. typedef struct { uint32_t id; char *asm_text; // mandatory char *op_str; int32_t is_alias; ///< 0 == not given, >0 == true, <0 == false uint64_t alias_id; char *mnemonic; TestDetail *details; } TestInsnData; TestInsnData *test_insn_data_new(); void test_insn_data_free(TestInsnData *test_insn_data); TestInsnData *test_insn_data_clone(TestInsnData *test_insn_data); static const cyaml_schema_field_t test_insn_data_mapping_schema[] = { CYAML_FIELD_UINT("id", CYAML_FLAG_SCALAR_PLAIN | CYAML_FLAG_OPTIONAL, TestInsnData, id), CYAML_FIELD_STRING_PTR("asm_text", CYAML_FLAG_POINTER, TestInsnData, asm_text, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR( "op_str", CYAML_FLAG_POINTER_NULL_STR | CYAML_FLAG_OPTIONAL, TestInsnData, op_str, 0, CYAML_UNLIMITED), CYAML_FIELD_UINT("is_alias", CYAML_FLAG_OPTIONAL, TestInsnData, is_alias), CYAML_FIELD_INT("alias_id", CYAML_FLAG_SCALAR_PLAIN | CYAML_FLAG_OPTIONAL, TestInsnData, alias_id), CYAML_FIELD_STRING_PTR( "mnemonic", CYAML_FLAG_POINTER_NULL_STR | CYAML_FLAG_OPTIONAL, TestInsnData, mnemonic, 0, CYAML_UNLIMITED), CYAML_FIELD_MAPPING_PTR( "details", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestInsnData, details, test_detail_mapping_schema), CYAML_FIELD_END }; /// The expected data for a test. This can hold multiple instructions /// if enough bytes were given. typedef struct { TestInsnData **insns; ///< Zero to N disassembled instructions. uint32_t insns_count; ///< Filled by cyaml. } TestExpected; TestExpected *test_expected_new(); void test_expected_free(TestExpected *test_expected); TestExpected *test_expected_clone(TestExpected *test_expected); void test_expected_compare(csh *handle, TestExpected *expected, cs_insn *insns, size_t insns_count, size_t arch_bits); static const cyaml_schema_value_t insn_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, TestInsnData, test_insn_data_mapping_schema), }; static const cyaml_schema_field_t test_expected_mapping_schema[] = { CYAML_FIELD_SEQUENCE("insns", CYAML_FLAG_POINTER, TestExpected, insns, &insn_schema, 0, CYAML_UNLIMITED), // 0-MAX options CYAML_FIELD_END }; /// A single test case. typedef struct { TestInput *input; ///< Input data for a test case TestExpected *expected; ///< Expected data of the test case. bool skip; ///< If set, the test is skipped char *skip_reason; ///< Reason this test is skipped. } TestCase; TestCase *test_case_new(); void test_case_free(TestCase *test_case); TestCase *test_case_clone(TestCase *test_case); static const cyaml_schema_field_t test_case_mapping_schema[] = { CYAML_FIELD_MAPPING_PTR("input", CYAML_FLAG_POINTER, TestCase, input, test_input_mapping_schema), CYAML_FIELD_MAPPING_PTR("expected", CYAML_FLAG_POINTER, TestCase, expected, test_expected_mapping_schema), CYAML_FIELD_BOOL("skip", CYAML_FLAG_OPTIONAL, TestCase, skip), CYAML_FIELD_STRING_PTR("skip_reason", CYAML_FLAG_POINTER_NULL_STR | CYAML_FLAG_OPTIONAL, TestCase, skip_reason, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const cyaml_schema_value_t test_case_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, TestCase, test_case_mapping_schema), }; typedef struct { char *filename; ///< Filename. NOT filled by cyaml. TestCase **test_cases; uint32_t test_cases_count; } TestFile; TestFile *test_file_new(); void test_file_free(TestFile *test_file); TestFile *test_file_clone(TestFile *test_file); static const cyaml_schema_field_t test_file_mapping_schema[] = { CYAML_FIELD_STRING_PTR( "filename", CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER_NULL_STR, TestFile, filename, 0, 0), CYAML_FIELD_SEQUENCE("test_cases", CYAML_FLAG_POINTER, TestFile, test_cases, &test_case_schema, 1, CYAML_UNLIMITED), // 1-MAX options CYAML_FIELD_END }; static const cyaml_schema_value_t test_file_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, TestFile, test_file_mapping_schema), }; #endif // TESTCASE_H