176 lines
5.8 KiB
C
Raw Normal View History

2024-09-07 18:00:09 +06:00
// Copyright © 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#ifndef TESTCASE_H
#define TESTCASE_H
#include "test_detail.h"
#include <cyaml/cyaml.h>
#include <capstone/capstone.h>
#include <stdbool.h>
#include <stdint.h>
/// 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