diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml
index ffcc4f4d..31b8afbd 100644
--- a/.github/workflows/builds.yml
+++ b/.github/workflows/builds.yml
@@ -8,9 +8,8 @@ on:
paths-ignore:
- '**.md'
-# TODO: fix skips
env:
- TORTURE_FLAGS: "--gen_crash_dump -s:d2r::d2r_line_table,d2r::d2r_checksums"
+ TORTURE_FLAGS: "* --gen_crash_dump"
jobs:
build-ubuntu-24-04:
diff --git a/run.bat b/run.bat
index 43a26831..a0f79b2e 100644
--- a/run.bat
+++ b/run.bat
@@ -27,6 +27,7 @@ if "%TORTURE_ARGS:~0,3%" == "-- " set "TORTURE_ARGS=%TORTURE_ARGS:~3%" && goto p
set "TORTURE_ARGS=%TORTURE_ARGS:* -- =%"
:parse_done
+if not defined TORTURE_ARGS set "TORTURE_ARGS=*
for %%m in (%MODE_VALUES%) do for %%c in (%CC_VALUES%) do (
setlocal
diff --git a/run.sh b/run.sh
index 39191afc..b7cb00c8 100755
--- a/run.sh
+++ b/run.sh
@@ -38,6 +38,10 @@ while [[ $# -gt 0 ]]; do
esac
done
+if [[ "${#torture_args[@]}" == "0" ]]; then
+ torture_args=("*")
+fi
+
for m in "${mode_values[@]}"; do
for c in "${cc_values[@]}"; do
# nuke artifacts from last run
diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c
index a47e8929..48534e14 100644
--- a/src/linker/hash_table.c
+++ b/src/linker/hash_table.c
@@ -926,7 +926,9 @@ internal void * keys_from_hash_map_raw ( Arena *arena, HashMap *hm) { retur
// values
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_value_u64) { MemoryCopy(buffer, &kv->value.value_u64, sizeof(kv->value.value_u64)); }
-internal U64 * values_from_hash_map_u64(Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, 0, 0, hash_map_extract_value_u64, sizeof(U64)).values; }
+force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_value_raw) { MemoryCopy(buffer, &kv->value.value_raw, sizeof(kv->value.value_raw)); }
+internal U64 * values_from_hash_map_u64(Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, 0, 0, hash_map_extract_value_u64, sizeof(U64)).values; }
+internal void * values_from_hash_map_raw(Arena *arena, HashMap *hm) {return hash_map_extract(arena, hm, 0, 0, hash_map_extract_value_raw, sizeof(void *)).values; }
////////////////////////////////
diff --git a/src/torture/dbg_tests/step_into_for_goto.c b/src/torture/dbg_tests/step_into_for_goto.c
new file mode 100644
index 00000000..5ff09613
--- /dev/null
+++ b/src/torture/dbg_tests/step_into_for_goto.c
@@ -0,0 +1,25 @@
+/// test: {
+/// windows: {
+/// skip: ""
+/// compile: {
+/// cc: "clang"
+/// args: "-O0 -g -o main.exe %FILE%"
+/// }
+/// launch: "main.exe"
+/// }
+/// linux: {
+/// skip: ""
+/// compile: "-O0 -g -o main %FILE%"
+/// launch: "./main"
+/// }
+/// }
+
+int main()
+{ /// 1: { step_into step_into }
+ for (int i = 0; i < 1; /// 2: step_into
+ ++i, ({ goto exit; })) { /// 4: step_into
+ int x = 0; /// 3: step_into
+ }
+ exit:;
+} /// 5: at
+
diff --git a/src/torture/dbg_tests/step_into_lazy_module_func.c b/src/torture/dbg_tests/step_into_lazy_module_func.c
index 07a6b089..fe289ba1 100644
--- a/src/torture/dbg_tests/step_into_lazy_module_func.c
+++ b/src/torture/dbg_tests/step_into_lazy_module_func.c
@@ -1,20 +1,22 @@
// TODO: step into alg needs to detect that it is about to step into a thunk and place breakpoint, respective to OS,
// where thunk calls into resolved function
-//
-// test: {
-// windows: {
-// compile: "/Od /Z7 /c main.c module.c"
-// link: "/fixed /debug:full /dll module.obj"
-// link: "/fixed /debug:full /entry:main kernel32.lib module.lib delayimp.lib main.obj /out:main.exe /delayload:module.dll /incremental:no"
-// launch: "main.exe"
-// }
-// linux: {
-// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so"
-// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%"
-// launch: "./main"
-// }
-// }
-//
+
+/// test: {
+/// windows: {
+/// skip: ""
+/// compile: "/Od /Z7 /c main.c module.c"
+/// link: "/fixed /debug:full /dll module.obj"
+/// link: "/fixed /debug:full /entry:main kernel32.lib module.lib delayimp.lib main.obj /out:main.exe /delayload:module.dll /incremental:no"
+/// launch: "main.exe"
+/// }
+/// linux: {
+/// skip: ""
+/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so"
+/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%"
+/// launch: "./main"
+/// }
+/// }
+///
/// file: "module.c"
diff --git a/src/torture/torture.c b/src/torture/torture.c
index 66e65d64..b399371f 100644
--- a/src/torture/torture.c
+++ b/src/torture/torture.c
@@ -23,19 +23,54 @@ global String8 g_output;
global String8 g_errors;
// tools
-global B32 g_dbg_graphical;
+global B32 g_gui;
global String8 g_radbin_path;
global String8 g_cl_path;
global String8 g_clang_path;
global String8 g_gcc_path;
global String8 g_linker_path;
+////////////////////////////////
+
+internal String8
+t_group_from_test(T_Test *test)
+{
+ String8 file = str8_cstring(test->file);
+ String8 group = str8_chop_last_dot(str8_skip_last_slash(file));
+ if (str8_match_wildcard(group, str8_lit("torture_*"), StringMatchFlag_CaseInsensitive)) {
+ group = str8_skip(group, str8_lit("torture_").size);
+ }
+ return group;
+}
+
+internal String8
+t_group_from_test_idx(U64 test_idx)
+{
+ return t_group_from_test(g_torture_tests[test_idx]);
+}
+
internal String8
t_test_name_from_idx(Arena *arena, U64 test_idx)
{
- return str8f(arena, "%s::%s", g_torture_tests[test_idx]->group, g_torture_tests[test_idx]->label);
+ return str8f(arena, "%.*s/%s", str8_varg(t_group_from_test_idx(test_idx)), g_torture_tests[test_idx]->label);
}
+internal String8List
+t_test_group_from_name(Arena *arena, String8 pattern)
+{
+ Temp scratch = scratch_begin(&arena, 1);
+ String8List matches = {0};
+ for EachIndex(i, g_torture_test_count) {
+ if (str8_match_wildcard(t_test_name_from_idx(scratch.arena, i), pattern, 0)) {
+ str8_list_push(arena, &matches, t_group_from_test_idx(i));
+ }
+ }
+ scratch_end(scratch);
+ return matches;
+}
+
+////////////////////////////////
+
internal char *
t_string_from_result(T_RunStatus v)
{
@@ -43,10 +78,24 @@ t_string_from_result(T_RunStatus v)
case T_RunStatus_Fail: return "FAIL";
case T_RunStatus_Crash: return "CRASH";
case T_RunStatus_Pass: return "PASS";
+ case T_RunStatus_Skip: return "SKIP";
+ default: break;
}
return 0;
}
+internal char *
+t_color_from_result(T_RunStatus v)
+{
+ switch (v) {
+#define X(n,c,...) case T_RunStatus_##n: return c;
+ T_Run_XList
+#undef X
+ default: break;
+ }
+ return "null";
+}
+
internal void
t_break_if_debugger_present(void)
{
@@ -167,17 +216,22 @@ t_run_caller(void *raw_ctx)
ctx->result.status = T_RunStatus_Pass;
String8List test_out = {0};
- ctx->run(scratch.arena, ctx->user_data, &ctx->result, &test_out);
+
+ if (ctx->test->skip) {
+ ctx->result.status = T_RunStatus_Skip;
+ } else {
+ ctx->test->r(scratch.arena, ctx->user_data, &ctx->result, &test_out);
+ }
if (ctx->result.status == T_RunStatus_Fail || ctx->result.status == T_RunStatus_Crash) {
for EachNode(n, String8Node, test_out.first) {
t_errorf("%S", n->string);
}
if (g_errors.size) {
- t_errorf("stderr: \"%S\"\n", g_errors);
+ t_errorf("%S\n", g_errors);
}
if (g_output.size) {
- t_errorf("stdout: \"%S\"\n", g_output);
+ t_errorf("%S\n", g_output);
}
}
@@ -185,9 +239,9 @@ t_run_caller(void *raw_ctx)
}
internal T_RunResult
-t_run(T_Run run, String8 user_data)
+t_run(T_Test *test, String8 user_data)
{
- T_RunCtx ctx = { .run = run, .user_data = user_data, .result.status = T_RunStatus_Fail };
+ T_RunCtx ctx = { .test = test, .user_data = user_data, .result.status = T_RunStatus_Fail };
t_run_caller(&ctx);
fflush(stdout);
fflush(stderr);
@@ -332,7 +386,7 @@ t_raddbg_path(void)
local_persist U8 buffer[4096];
ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer };
Arena *arena = arena_alloc_(¶ms);
- String8 raddbg_base_name = g_dbg_graphical ? str8_lit("raddbg") : str8_lit("raddbg_non_graphical");
+ String8 raddbg_base_name = g_gui ? str8_lit("raddbg") : str8_lit("raddbg_non_graphical");
#if OS_WINDOWS
Temp scratch = scratch_begin(0, 0);
path = full_path_from_path(arena, str8f(scratch.arena, "%S.exe", raddbg_base_name));
@@ -779,7 +833,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
exit:;
for EachElement(i, read_capture_handles) { file_close(read_capture_handles[i]); }
for EachElement(i, write_capture_handles) { file_close(write_capture_handles[i]); }
- AssertAlways(is_ok);
+
scratch_end(scratch);
return is_ok;
}
@@ -883,7 +937,9 @@ internal int
t_test_compar(const void *raw_a, const void *raw_b)
{
const T_Test *a = raw_a, *b = raw_b;
- int cmp = str8_compar(str8_cstring(a->group), str8_cstring(b->group), 0);
+ String8 group_a = t_group_from_test((T_Test *)a);
+ String8 group_b = t_group_from_test((T_Test *)b);
+ int cmp = str8_compar(group_a, group_b, 0);
if (cmp == 0) {
cmp = u64_compar(&a->decl_line, &b->decl_line);
}
@@ -1014,10 +1070,37 @@ t_errorf(char *fmt, ...)
scratch_end(scratch);
}
+internal void
+t_help(void)
+{
+ fprintf(stderr, "--- Help -------------------------------------------------------\n");
+ fprintf(stderr, " %s\n\n", BUILD_TITLE_STRING_LITERAL);
+ fprintf(stderr, " Usage: torture [Options] \n\n");
+ fprintf(stderr, " Options:\n");
+ fprintf(stderr, " -list Print available test targets\n");
+ fprintf(stderr, " --gui Launch debugger with window\n");
+ fprintf(stderr, " -cl: Override default cl path\n");
+ fprintf(stderr, " -clang: Override default clang path\n");
+ fprintf(stderr, " -gcc: Override default gcc path\n");
+ fprintf(stderr, " -linker: Path to PE/COFF linker\n");
+ fprintf(stderr, " -print_stdout Print to console stdout and stderr of a run\n");
+ fprintf(stderr, " -out: Directory path for test outputs (default \"%.*s\")\n", str8_varg(g_out));
+ fprintf(stderr, " -verbose Enable verbose mode\n");
+ fprintf(stderr, " -help Print help menu and exit\n");
+ fprintf(stderr, "\nInputs are wildcard expressions. Prefix with ! to skip matches, or + to force-run matches.\n");
+ fprintf(stderr, "\nExamples:\n");
+ fprintf(stderr, " torture * Run all tests\n");
+ fprintf(stderr, " torture bit_array Run 'bit_array' test\n");
+ fprintf(stderr, " torture !* Skip all tests\n");
+ fprintf(stderr, " torture * !bit_array Run all tests but skip 'bit_array'\n");
+ fprintf(stderr, " torture +* Force-run all tests\n");
+}
+
internal void
t_entry_point(CmdLine *cmdline)
{
Temp scratch = scratch_begin(0,0);
+ U64 exit_code = max_U64;
U64 dashes_size = 9999;
U8 *dashes = push_array(scratch.arena, U8, dashes_size);
@@ -1036,25 +1119,10 @@ t_entry_point(CmdLine *cmdline)
//
{
B32 print_help = cmd_line_has_flag(cmdline, str8_lit("help")) ||
- cmd_line_has_flag(cmdline, str8_lit("h"));
+ cmd_line_has_flag(cmdline, str8_lit("h"));
if (print_help) {
- PrintHeader("Help");
- fprintf(stderr, " %s\n\n", BUILD_TITLE_STRING_LITERAL);
- fprintf(stderr, " Usage: torture [Options] [Files]\n\n");
- fprintf(stderr, " Options:\n");
- fprintf(stderr, " -target:{name[,name]} Selects targets to test\n");
- fprintf(stderr, " -skip:{name[,name]} Selects targets to skip\n");
- fprintf(stderr, " -list Print available test targets and exit\n");
- fprintf(stderr, " -out:{path} Directory path for test outputs (default \"%.*s\")\n", str8_varg(g_out));
- fprintf(stderr, " -verbose Enable verbose mode\n");
- fprintf(stderr, " -cl:{path} Override default cl path\n");
- fprintf(stderr, " -clang:{path} Override default clang path\n");
- fprintf(stderr, " -gcc:{path} Override default gcc path\n");
- fprintf(stderr, " -linker:{path} Path to PE/COFF linker\n");
- fprintf(stderr, " --dbg_graphical Launch debugger with window\n");
- fprintf(stderr, " -print_stdout Print to console stdout and stderr of a run\n");
- fprintf(stderr, " -help Print help menu and exit\n");
- abort_self(0);
+ t_help();
+ goto exit;
}
}
@@ -1086,7 +1154,7 @@ t_entry_point(CmdLine *cmdline)
//
// Compiler overrides
//
- g_dbg_graphical = cmd_line_has_flag(cmdline, str8_lit("dbg_graphical"));
+ g_gui = cmd_line_has_flag(cmdline, str8_lit("gui"));
g_cl_path = cmd_line_string(cmdline, str8_lit("cl"));
g_clang_path = cmd_line_string(cmdline, str8_lit("clang"));
g_gcc_path = cmd_line_string(cmdline, str8_lit("gcc"));
@@ -1106,44 +1174,68 @@ t_entry_point(CmdLine *cmdline)
//
// Handle optional -target
//
- String8List target = cmdline->inputs;
+ U64List targets = {0};
{
- CmdLineOpt *target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target"));
- if (target_opt == 0) {
- target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t"));
- }
+ String8List inputs = {0};
+
+ CmdLineOpt *target_opt = 0;
+ if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target")); }
+ if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t")); }
+
+ // handle explicit target switch
if (target_opt) {
- HashTable *ht = hash_table_init(scratch.arena, g_torture_test_count*2);
+ str8_list_concat_in_place(&inputs, &target_opt->value_strings);
+ }
- String8List targets = target_opt->value_strings;
- str8_list_concat_in_place(&targets, &cmdline->inputs);
+ // accept inputs from the command line as target tests to run
+ str8_list_concat_in_place(&inputs, &cmdline->inputs);
- if (targets.node_count > 0) {
- for EachNode(pattern_n, String8Node, targets.first) {
- B32 do_namespace = str8_find_needle(pattern_n->string, 0, str8_lit("::"), 0) < pattern_n->string.size;
- for EachIndex(test_idx, g_torture_test_count) {
- String8 name = str8_cstring(g_torture_tests[test_idx]->label);
- if (do_namespace) {
- name = t_test_name_from_idx(scratch.arena, test_idx);
- }
- if (str8_match_wildcard(name, pattern_n->string, 0)) {
- if ( ! hash_table_search_string(ht, name)) {
- hash_table_push_string_raw(scratch.arena, ht, name, 0);
- str8_list_push(scratch.arena, &target, str8_cstring(g_torture_tests[test_idx]->label));
- }
- }
+ // no inputs -> print help and exit
+ if (inputs.node_count == 0) {
+ t_help();
+ goto exit;
+ }
+
+ HashMap hm = {0};
+ for EachNode(input_n, String8Node, inputs.first) {
+ String8 t = input_n->string;
+
+ // parse mode
+ typedef enum { Mode_Default, Mode_Skip, Mode_Force, } Mode;
+ Mode mode = Mode_Default;
+ if (str8_match_wildcard(t, str8_lit("+*"), 0)) { mode = Mode_Force; t = str8_skip(t, 1); }
+ else if (str8_match_wildcard(t, str8_lit("!*"), 0)) { mode = Mode_Skip; t = str8_skip(t, 1); }
+
+ if (str8_find_needle(t, 0, str8_lit("/"), 0) >= t.size) {
+ t = str8f(scratch.arena, "*/%S", t);
+ }
+
+ U64 match_count = 0;
+
+ for EachIndex(test_idx, g_torture_test_count) {
+ // match test names
+ String8 test_name = t_test_name_from_idx(scratch.arena, test_idx);
+
+ if (str8_match_wildcard(test_name, t, StringMatchFlag_CaseInsensitive)) {
+ // set skip flag
+ switch (mode) {
+ case Mode_Default: break;
+ case Mode_Skip: g_torture_tests[test_idx]->skip = 1; break;
+ case Mode_Force: g_torture_tests[test_idx]->skip = 0; break;
}
- }
-
- if (ht->count == 0) {
- fprintf(stderr, "ERROR: -target matches not found for the following patterns: ");
- for EachNode(n, String8Node, target_opt->value_strings.first) {
- fprintf(stderr, "\"%.*s\"\n", str8_varg(n->string));
+
+ // append test when not in skipping mode
+ if ( ! hash_map_search_string_u64(&hm, test_name)) {
+ hash_map_push_string_u64(scratch.arena, &hm, test_name, 1);
+ u64_list_push(scratch.arena, &targets, test_idx);
}
- abort_self(1);
+
+ match_count += 1;
}
- } else {
- fprintf(stderr, "ERROR: -target has invalid number of arguments\n");
+ }
+
+ if (match_count == 0) {
+ fprintf(stderr, "WARNING: no matches found for input: %.*s\n", str8_varg(input_n->string));
}
}
}
@@ -1199,62 +1291,17 @@ t_entry_point(CmdLine *cmdline)
// Run tests
//
{
- U64List target_indices_list = {0};
- if (target.node_count == 0) {
- for EachIndex(i, g_torture_test_count) { u64_list_push(scratch.arena, &target_indices_list, i); }
- } else {
- for EachNode(target_n, String8Node, target.first) {
- B32 is_target_unknown = 1;
- for EachIndex(i, g_torture_test_count) {
- if (str8_match(str8_cstring(g_torture_tests[i]->label), target_n->string, 0)) {
- u64_list_push(scratch.arena, &target_indices_list, i);
- is_target_unknown = 0;
- break;
- }
- }
- if (is_target_unknown) {
- fprintf(stderr, "ERROR: unknown target \"%.*s\"\n", str8_varg(target_n->string));
- }
- }
- }
-
- //
- // -skip
- //
- U64List final_target_list = {0};
- CmdLineOpt *skip_opt = cmd_line_opt_from_string(cmdline, str8_lit("skip"));
- CmdLineOpt *s_opt = cmd_line_opt_from_string(cmdline, str8_lit("s"));
- String8List skip_list = skip_opt ? skip_opt->value_strings : s_opt ? s_opt->value_strings : (String8List){0};
- for EachNode(n, U64Node, target_indices_list.first) {
- // should test be skipped?
- B32 include_test = 1;
- String8 test_name = t_test_name_from_idx(scratch.arena, n->data);
- for EachNode(pattern_n, String8Node, skip_list.first) {
- if (str8_match_wildcard(test_name, pattern_n->string, 0)) {
- include_test = 0;
- break;
- }
- }
-
- if (include_test) {
- u64_list_push(scratch.arena, &final_target_list, n->data);
- }
- }
-
- U64 skip_count = target_indices_list.count - final_target_list.count;
- U64Array target_indices = u64_array_from_list(scratch.arena, &final_target_list);
+ U64Array target_indices = u64_array_from_list(scratch.arena, &targets);
U64 max_label_size = 0;
U64 max_group_size = 0;
for EachIndex(i, target_indices.count) {
U64 test_idx = target_indices.v[i];
max_label_size = Max(max_label_size, cstring8_length((U8*)g_torture_tests[test_idx]->label));
- max_group_size = Max(max_group_size, cstring8_length((U8*)g_torture_tests[test_idx]->group));
+ max_group_size = Max(max_group_size, t_group_from_test_idx(test_idx).size);
}
- U64 pass_count = 0;
- U64 fail_count = 0;
- U64 crash_count = 0;
+ U64 run_counters[T_RunStatus_Count] = {0};
U64 max_digit_count = count_digits_u64(target_indices.count, 10);
U64 total_time_start = now_time_us();
@@ -1262,6 +1309,8 @@ t_entry_point(CmdLine *cmdline)
Slowest slowest[5] = {0};
for EachElement(i, slowest) { slowest[i].target_idx = max_U64; }
+ U64List skipped_tests = {0};
+
for EachIndex(i, target_indices.count) {
if (i == 0) { PrintHeader("Tests"); }
@@ -1274,7 +1323,7 @@ t_entry_point(CmdLine *cmdline)
U64 curr_digit_count = count_digits_u64(i+1, 10);
int idx_align_space_count = (int)(max_digit_count - curr_digit_count);
fprintf(stdout, "[%.*s%llu/%llu] ", idx_align_space_count, spaces, (unsigned long long)i+1, (unsigned long long)target_indices.count);
- fprintf(stdout, "%s %.*s:: %s", test->group, (int)(max_group_size - cstring8_length((U8*)test->group)), spaces, test->label);
+ fprintf(stdout, "%.*s %.*s/ %s", str8_varg(t_group_from_test(test)), (int)(max_group_size - t_group_from_test(test).size), spaces, test->label);
fprintf(stdout, " %.*s ", (int)dots_count, dots);
fflush(stdout);
@@ -1295,19 +1344,14 @@ t_entry_point(CmdLine *cmdline)
// run test
U64 run_start_time = now_time_us();
- T_RunResult result = t_run(test->r, test->user_data);
+ T_RunResult result = t_run(test, test->user_data);
U64 run_end_time = now_time_us();
- // print result
- if (result.status == T_RunStatus_Pass) {
- fprintf(stdout, "\x1b[32m" "%s" "\x1b[0m", t_string_from_result(result.status));
- pass_count += 1;
- } else if (result.status == T_RunStatus_Fail) {
- fprintf(stdout, "\x1b[31m" "%s" "\x1b[0m", t_string_from_result(result.status));
- fail_count += 1;
- } else if (result.status == T_RunStatus_Crash) {
- fprintf(stdout, "\x1b[33m" "%s" "\x1b[0m", t_string_from_result(result.status));
- crash_count += 1;
- }
+
+ // update
+ run_counters[result.status] += 1;
+
+ // print run status
+ fprintf(stdout, "%s%s" T_RESET, t_color_from_result(result.status), t_string_from_result(result.status));
if (result.status == T_RunStatus_Pass) {
U64 d = run_end_time - run_start_time;
@@ -1341,18 +1385,23 @@ t_entry_point(CmdLine *cmdline)
if (result.status == T_RunStatus_Fail || result.status == T_RunStatus_Crash) {
if (g_stop_on_first_fail_or_crash) { goto exit; }
}
+
+ if (result.status == T_RunStatus_Skip) {
+ u64_list_push(scratch.arena, &skipped_tests, target_idx);
+ }
}
U64 total_time_end = now_time_us();
- if (target_indices.count > 0 && (pass_count > 0 || fail_count > 0 || crash_count > 0 || skip_count > 0)) {
+ if (target_indices.count > 0 && sum_array_u64(ArrayCount(run_counters), run_counters) > 0) {
+ U64 total_time_dt = total_time_end - total_time_start;
+ String8 total_time_str = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(total_time_dt));
+
fprintf(stderr, "\n");
PrintHeader("Summary");
- U64 total_time_dt = total_time_end - total_time_start;
- String8 total_time_str = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(total_time_dt));
- fprintf(stderr, " Passed %llu\n", (unsigned long long)pass_count);
- fprintf(stderr, " Failed %llu\n", (unsigned long long)fail_count);
- fprintf(stderr, " Crashed %llu\n", (unsigned long long)crash_count);
- fprintf(stderr, " Skipped %llu\n", (unsigned long long)skip_count);
+ fprintf(stderr, " Passed %llu\n", (unsigned long long)run_counters[T_RunStatus_Pass]);
+ fprintf(stderr, " Failed %llu\n", (unsigned long long)run_counters[T_RunStatus_Fail]);
+ fprintf(stderr, " Crashed %llu\n", (unsigned long long)run_counters[T_RunStatus_Crash]);
+ fprintf(stderr, " Skipped %llu\n", (unsigned long long)run_counters[T_RunStatus_Skip]);
fprintf(stderr, " Time %.*s\n", str8_varg(total_time_str));
U64 slow_count = 0;
@@ -1368,17 +1417,17 @@ t_entry_point(CmdLine *cmdline)
for EachElement(i, slowest) {
Slowest s = slowest[i];
label_max = Max(strlen(g_torture_tests[s.target_idx]->label), label_max);
- group_max = Max(strlen(g_torture_tests[s.target_idx]->group), group_max);
+ group_max = Max(t_group_from_test_idx(s.target_idx).size, group_max);
}
- fprintf(stderr, " Slow Tests\n");
+ fprintf(stderr, " \nSlow Tests\n");
for EachElement(i, slowest) {
Slowest s = slowest[i];
if (s.target_idx >= g_torture_test_count) { break; }
String8 elapsed_time = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(s.d));
- fprintf(stderr, " %s %.*s:: %s %.*s %.*s\n",
- g_torture_tests[s.target_idx]->group,
- (int)(group_max - strlen(g_torture_tests[s.target_idx]->group)), spaces,
+ fprintf(stderr, " %.*s %.*s/ %s %.*s %.*s\n",
+ str8_varg(t_group_from_test_idx(s.target_idx)),
+ (int)(group_max - t_group_from_test_idx(s.target_idx).size), spaces,
g_torture_tests[s.target_idx]->label,
(int)(label_max - strlen(g_torture_tests[s.target_idx]->label)) + 4, dots,
str8_varg(elapsed_time));
@@ -1386,12 +1435,10 @@ t_entry_point(CmdLine *cmdline)
}
}
+ exit_code = run_counters[T_RunStatus_Fail] + run_counters[T_RunStatus_Crash];
exit:;
- if (fail_count + crash_count != 0) {
- fflush(stdout);
- abort_self(fail_count + crash_count);
- }
}
-
+
scratch_end(scratch);
+ // TODO: return exit_code;
}
diff --git a/src/torture/torture.h b/src/torture/torture.h
index 80a5995d..fff10f90 100644
--- a/src/torture/torture.h
+++ b/src/torture/torture.h
@@ -5,11 +5,25 @@
////////////////////////////////
+#define T_RESET "\x1b[0m"
+#define T_RED "\x1b[31m"
+#define T_GREEN "\x1b[32m"
+#define T_YELLOW "\x1b[33m"
+#define T_BLUE "\x1b[34m"
+
+//#define X(n, c)
+#define T_Run_XList \
+ X(Fail, T_RED) \
+ X(Crash, T_RED) \
+ X(Pass, T_GREEN) \
+ X(Skip, T_RESET)
+
typedef enum
{
- T_RunStatus_Fail,
- T_RunStatus_Crash,
- T_RunStatus_Pass,
+#define X(n,...) T_RunStatus_##n,
+T_Run_XList
+#undef X
+ T_RunStatus_Count
} T_RunStatus;
typedef struct
@@ -25,20 +39,21 @@ typedef void (*T_Run)(Arena *arena, String8 user_data, T_RunResul
typedef struct
{
- T_Run run;
- String8 user_data;
- T_RunResult result;
-} T_RunCtx;
-
-typedef struct
-{
- char *group;
+ char *file;
char *label;
int decl_line;
T_Run r;
+ B32 skip;
String8 user_data;
} T_Test;
+typedef struct
+{
+ T_Test *test;
+ String8 user_data;
+ T_RunResult result;
+} T_RunCtx;
+
typedef enum
{
T_Compiler_Null,
@@ -61,25 +76,28 @@ extern T_Test g_torture_tests_[0xffffff];
internal void t_break_if_debugger_present(void);
-#define T_AddTest(name, l, ...) \
+#define T_AddTest(name, f, l, skip, ...) \
T_RunSig(name); \
__VA_ARGS__ void t_add_test_##name(void) \
{ \
- g_torture_tests_[g_torture_test_count++] = (T_Test){ .group = T_Group, .label = Stringify(name), .r = &t_##name, .decl_line = l }; \
+ g_torture_tests_[g_torture_test_count++] = (T_Test){ f, Stringify(name), l, &t_##name, skip }; \
}
#if COMPILER_MSVC
# pragma section(".CRT$XCU", read)
-# define TEST_(name) \
- T_AddTest(name, __LINE__) \
+# define TEST_(name, skip) \
+ T_AddTest(name, __FILE__, __LINE__, skip) \
__declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \
__pragma(comment(linker, "/include:" Stringify(r_##name)))
#else
-# define TEST_(name) T_AddTest(name, __LINE__, __attribute__((constructor)))
+# define TEST_(name, skip) T_AddTest(name, __FILE__, __LINE__, skip, __attribute__((constructor)))
#endif
#define TEST(name) \
- TEST_(name) \
+ TEST_(name, 0) \
+ T_RunSig(name)
+#define SKIP(name) \
+ TEST_(name, 1) \
T_RunSig(name)
#define T_Ok(c) do { if (!(c)) { \
@@ -113,7 +131,7 @@ internal String8 t_make_file_path(Arena *arena, String8 name);
// test runner
internal void t_run_caller(void *raw_ctx);
internal void t_run_fail_handler(void *raw_ctx);
-internal T_RunResult t_run(T_Run run, String8 user_data);
+internal T_RunResult t_run(T_Test *test, String8 user_data);
// tools
internal String8 t_radbin_path(void);
diff --git a/src/torture/torture_base.c b/src/torture/torture_base.c
index 12929bf1..988378d8 100644
--- a/src/torture/torture_base.c
+++ b/src/torture/torture_base.c
@@ -1,8 +1,6 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
-#define T_Group "Base"
-
TEST(str8_list_substr)
{
String8List zero_list = {0};
@@ -341,4 +339,3 @@ TEST(hash_map)
}
}
-#undef T_Group
diff --git a/src/torture/torture_d2r.c b/src/torture/torture_d2r.c
index bdc43c3d..5576f7ea 100644
--- a/src/torture/torture_d2r.c
+++ b/src/torture/torture_d2r.c
@@ -5,8 +5,6 @@
//
// [ ] d2r_types alias size and byte size on __float80 typedef mismatch
-#define T_Group "d2r"
-
internal RDI_Parsed *
d2r_rdi_from_dwarf_writer(Arena *arena, DW_Writer *writer)
{
@@ -232,7 +230,7 @@ T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), s
dw_writer_end(&writer);
}
-TEST(d2r_line_table)
+SKIP(d2r_line_table)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
String8 comp_dir = str8_lit("c:/DEVEL/");
@@ -303,7 +301,7 @@ TEST(d2r_line_table)
dw_writer_end(&writer);
}
-TEST(d2r_checksums)
+SKIP(d2r_checksums)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
@@ -553,4 +551,3 @@ TEST(d2r_general)
dw_writer_end(&writer);
}
-#undef T_Group
diff --git a/src/torture/torture_dbg.c b/src/torture/torture_dbg.c
index 9b8b9bc0..e92c6e51 100644
--- a/src/torture/torture_dbg.c
+++ b/src/torture/torture_dbg.c
@@ -1,32 +1,54 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
-#define T_Group "Dbg"
-
#define T_Dbg_DefaultTimeout TIMEOUT_SEC(5)
extern B32 g_stop_on_first_fail_or_crash;
+////////////////////////////////
+
+internal void
+t_find_line_and_col(String8 source, MD_Node *n, U64 *line_out, U64 *col_out)
+{
+ U64 line = 1;
+ U64 col = 1;
+ for (U64 cursor = 0; cursor < source.size && cursor < n->src_offset; cursor += 1) {
+ if (source.str[cursor] == '\n') {
+ line += 1;
+ col = 0;
+ }
+ col += 1;
+ }
+ if (line_out) { *line_out = line; }
+ if (col_out) { *col_out = col; }
+}
+
+internal void
+t_errorf_md(String8 file_name, String8 source, MD_Node *n, char *fmt, ...)
+{
+ Temp scratch = scratch_begin(0,0);
+ va_list args;
+ va_start(args, fmt);
+ String8 result = push_str8fv(scratch.arena, fmt, args);
+ U64 line = 0, col = 0;
+ t_find_line_and_col(source, n, &line, &col);
+ t_errorf("ERROR: %S:%llu%llu: %S\n", file_name, (unsigned long long)line, (unsigned long long)col, result);
+ va_end(args);
+ scratch_end(scratch);
+}
+
////////////////////////////////
// Debugger IPC Replies
-typedef struct RD_IpcReply RD_IpcReply;
-struct RD_IpcReply
+typedef struct
{
MD_ParseResult parse;
MD_Node *root;
MD_Node *msg;
-};
-
-internal RD_IpcReply
-rd_ipc_mdesk_reply_from_string(Arena *arena, String8 string)
-{
- MD_ParseResult parse = md_parse_from_text(arena, str8_lit("ipc_reply"), string);
- return (RD_IpcReply){ .parse = parse, .root = parse.root, parse.root->first };
-}
+} T_IpcReply;
internal B32
-rd_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out)
+t_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out)
{
MD_Node *child = md_child_from_string(node, child_name, 0);
if (!md_node_is_nil(child) && !md_node_is_nil(child->first))
@@ -38,10 +60,10 @@ rd_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out)
}
internal B32
-rd_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out)
+t_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out)
{
String8 value = {0};
- if (rd_ipc_parse_string(node, child_name, &value))
+ if (t_ipc_parse_string(node, child_name, &value))
{
U64 v64 = 0;
if (try_u64_from_str8_c_rules(value, &v64))
@@ -53,12 +75,12 @@ rd_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out)
return 0;
}
-#define rd_ipc_parse_int(n, c, ptr) rd_ipc_parse_int_(n, c, sizeof(*ptr), ptr)
+#define t_ipc_parse_int(n, c, ptr) t_ipc_parse_int_(n, c, sizeof(*ptr), ptr)
internal B32
-rd_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out)
+t_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out)
{
String8 value = {0};
- if (rd_ipc_parse_string(node, child_name, &value))
+ if (t_ipc_parse_string(node, child_name, &value))
{
U64 v64 = 0;
if (try_u64_from_str8_c_rules(value, &v64))
@@ -71,12 +93,12 @@ rd_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out)
}
internal B32
-rd_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out)
+t_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out)
{
B32 is_ok = 0;
String8 s = {0};
U64 value = 0;
- if (rd_ipc_parse_string(node, child_name, &s))
+ if (t_ipc_parse_string(node, child_name, &s))
{
if (str8_matchi(s, str8_lit("true"))) { value = 1; is_ok = 1; }
else if (str8_matchi(s, str8_lit("false"))) { value = 0; is_ok = 1; }
@@ -172,9 +194,9 @@ t_dbg_state(Arena *arena, U64 timeout_us)
MD_Node *threads_md = md_child_from_string(state_md, str8_lit("threads"), 0);
MD_Node *modules_md = md_child_from_string(state_md, str8_lit("modules"), 0);
T_DbgState v = {0};
- if (!rd_ipc_parse_int(state_md, str8_lit("running"), &v.running)) { t_infof("INFO: 'state' is missing 'running'\n"); goto exit; }
- if (!rd_ipc_parse_int(state_md, str8_lit("run_gen"), &v.run_gen)) { t_infof("INFO: 'state' is missing 'run_gen'\n"); goto exit; }
- if (!rd_ipc_parse_int(state_md, str8_lit("ip"), &v.ip)) { t_infof("INFO: 'state' is missing 'ip'\n"); goto exit; }
+ if (!t_ipc_parse_int(state_md, str8_lit("running"), &v.running)) { t_infof("INFO: 'state' is missing 'running'\n"); goto exit; }
+ if (!t_ipc_parse_int(state_md, str8_lit("run_gen"), &v.run_gen)) { t_infof("INFO: 'state' is missing 'run_gen'\n"); goto exit; }
+ if (!t_ipc_parse_int(state_md, str8_lit("ip"), &v.ip)) { t_infof("INFO: 'state' is missing 'ip'\n"); goto exit; }
result = push_array(arena, T_DbgState, 1);
*result = v;
@@ -201,11 +223,11 @@ t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_u
U64 line_count = 0;
for MD_EachNode(n, lines_md->first) {
T_DbgLine line = {0};
- if ( ! rd_ipc_parse_string(n, str8_lit("file_path"), &line.file_path)) { t_infof("INFO: 'lines' is missing 'file_path'\n"); goto exit; }
- if ( ! rd_ipc_parse_int(n, str8_lit("line_num"), &line.line_num)) { t_infof("INFO: 'lines' is missing 'line_num'\n"); goto exit; }
- if ( ! rd_ipc_parse_int(n, str8_lit("column_num"), &line.column_num)) { t_infof("INFO: 'lines' is missing 'column_num'\n"); goto exit; }
- if ( ! rd_ipc_parse_int(n, str8_lit("voff_range_min"), &line.voff_range.min)) { t_infof("INFO: 'lines' is missing 'voff_range_min'\n"); goto exit; }
- if ( ! rd_ipc_parse_int(n, str8_lit("voff_range_max"), &line.voff_range.max)) { t_infof("INFO: 'lines' is missing 'voff_range_max'\n"); goto exit; }
+ if ( ! t_ipc_parse_string(n, str8_lit("file_path"), &line.file_path)) { t_infof("INFO: 'lines' is missing 'file_path'\n"); goto exit; }
+ if ( ! t_ipc_parse_int(n, str8_lit("line_num"), &line.line_num)) { t_infof("INFO: 'lines' is missing 'line_num'\n"); goto exit; }
+ if ( ! t_ipc_parse_int(n, str8_lit("column_num"), &line.column_num)) { t_infof("INFO: 'lines' is missing 'column_num'\n"); goto exit; }
+ if ( ! t_ipc_parse_int(n, str8_lit("voff_range_min"), &line.voff_range.min)) { t_infof("INFO: 'lines' is missing 'voff_range_min'\n"); goto exit; }
+ if ( ! t_ipc_parse_int(n, str8_lit("voff_range_max"), &line.voff_range.max)) { t_infof("INFO: 'lines' is missing 'voff_range_max'\n"); goto exit; }
Node *n = push_array(scratch.arena, Node, 1);
n->v = line;
@@ -393,10 +415,10 @@ t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out)
B32 is_ok = t_dbg_send_cmd(cmd, T_Dbg_DefaultTimeout, arena, &reply);
T_Eval e = {0};
- if ( ! rd_ipc_parse_string(reply.root, str8_lit("expr"), &e.expr)) { t_errorf("ERROR: failed to parse reply member: expr\n"); Assert(0); goto exit; }
- if ( ! rd_ipc_parse_string(reply.root, str8_lit("value"), &e.value)) { t_errorf("ERROR: failed to parse reply member: value\n"); Assert(0); goto exit; }
- if ( ! rd_ipc_parse_string(reply.root, str8_lit("type"), &e.type)) { t_errorf("ERROR: failed to parse reply member: type\n"); Assert(0); goto exit; }
- if ( ! rd_ipc_parse_string(reply.root, str8_lit("error"), &e.error)) { t_errorf("ERROR: failed to parse reply member: error\n"); Assert(0); goto exit; }
+ if ( ! t_ipc_parse_string(reply.root, str8_lit("expr"), &e.expr)) { t_errorf_md(str8_lit("IPC"), str8_zero(), reply.root, "ERROR: failed to parse reply member: expr\n"); Assert(0); goto exit; }
+ if ( ! t_ipc_parse_string(reply.root, str8_lit("value"), &e.value)) { t_errorf_md(str8_lit("IPC"), str8_zero(), reply.root, "ERROR: failed to parse reply member: value\n"); Assert(0); goto exit; }
+ if ( ! t_ipc_parse_string(reply.root, str8_lit("type"), &e.type)) { t_errorf_md(str8_lit("IPC"), str8_zero(), reply.root, "ERROR: failed to parse reply member: type\n"); Assert(0); goto exit; }
+ if ( ! t_ipc_parse_string(reply.root, str8_lit("error"), &e.error)) { t_errorf_md(str8_lit("IPC"), str8_zero(), reply.root, "ERROR: failed to parse reply member: error\n"); Assert(0); goto exit; }
if (eval_out) { *eval_out = e; }
exit:;
@@ -424,22 +446,11 @@ internal String8
t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v)
{
switch (v) {
- case T_DbgScriptCmdKind_Null: return str8_zero();
- case T_DbgScriptCmdKind_Breakpoint: return str8_lit("bp");
- case T_DbgScriptCmdKind_ClearBreakpoints: return str8_lit("bp_clear");
- case T_DbgScriptCmdKind_Run: return str8_lit("run");
- case T_DbgScriptCmdKind_Halt: return str8_lit("halt");
- case T_DbgScriptCmdKind_StepOver: return str8_lit("step_over");
- case T_DbgScriptCmdKind_StepInto: return str8_lit("step_into");
- case T_DbgScriptCmdKind_StepOut: return str8_lit("step_out");
- case T_DbgScriptCmdKind_StepOverInst: return str8_lit("step_over_inst");
- case T_DbgScriptCmdKind_StepIntoInst: return str8_lit("step_into_inst");
- case T_DbgScriptCmdKind_StepOverLine: return str8_lit("step_over_line");
- case T_DbgScriptCmdKind_StepIntoLine: return str8_lit("step_into_line");
- case T_DbgScriptCmdKind_KillAll: return str8_lit("kill_all");
- case T_DbgScriptCmdKind_At: return str8_lit("at");
- case T_DbgScriptCmdKind_Eval: return str8_lit("eval");
- default: InvalidPath;
+#define X(n,v) case T_DbgScriptCmdKind_##n: return str8_lit(v);
+ T_DbgScriptCmdKind_XList
+#undef X
+ case T_DbgScriptCmdKind_Null: return str8_zero();
+ default: InvalidPath; break;
}
return str8_zero();
}
@@ -473,9 +484,9 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
String8 comment = str8_skip_chop_whitespace(token_string);
String8 prefix = str8_lit("///");
if (str8_matchi(str8_prefix(comment, prefix.size), prefix)) {
- String8 script_part = str8_skip(comment, prefix.size);
- U64 script_part_base_off = (U64)(script_part.str - source.str);
- MD_TokenizeResult script_part_tokenize = md_tokenize_from_text(scratch.arena, script_part);
+ String8 script_part = str8_skip(comment, prefix.size);
+ U64 script_part_base_off = (U64)(script_part.str - source.str);
+ MD_TokenizeResult script_part_tokenize = md_tokenize_from_text(scratch.arena, script_part);
for EachIndex(script_token_idx, script_part_tokenize.tokens.count) {
MD_Token script_token = script_part_tokenize.tokens.v[script_token_idx];
script_token.range.min += script_part_base_off;
@@ -508,28 +519,65 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
if (str8_matchi(field->string, str8_lit("compile"))) { kind = T_DbgScriptDirectiveKind_Compile; }
else if (str8_matchi(field->string, str8_lit("link"))) { kind = T_DbgScriptDirectiveKind_Link; }
else if (str8_matchi(field->string, str8_lit("launch"))) { kind = T_DbgScriptDirectiveKind_Launch; }
- AssertAlways(kind != T_DbgScriptDirectiveKind_Null);
-
- // syntax check
- AssertAlways( !md_node_is_nil(field->first));
- AssertAlways(md_node_is_nil(field->first->next));
- Assert(field->first->flags & MD_NodeFlag_StringLiteral);
-
- // src_offset -> line
- //
- // TODO: super silly!! mdesk should export line numbers
- U64 line = 1;
- String8 text_before_src = str8_prefix(source, field->src_offset);
- for EachIndex(idx, text_before_src.size) { line += (text_before_src.str[idx] == '\n'); }
-
- T_DbgScriptDirective *n = push_array(arena, T_DbgScriptDirective, 1);
- n->kind = kind;
- n->line = line;
- n->args = str8_copy(arena, field->first->string);
- // TODO: expand % in compile: and link: to current source file name and esacpe with %%
-
+ else if (str8_matchi(field->string, str8_lit("skip"))) { kind = T_DbgScriptDirectiveKind_Skip; }
+
+ if (kind == T_DbgScriptDirectiveKind_Null) {
+ t_errorf_md(file_path, source, n, "unknown field in test header \"%S\"\n", field->string);
+ goto exit;
+ }
+
+ T_DbgScriptDirective *dir = push_array(arena, T_DbgScriptDirective, 1);
+ dir->kind = kind;
+ t_find_line_and_col(source, field, &dir->line, 0);
+
+ if (field->flags & MD_NodeFlag_HasBraceLeft) {
+ if (kind == T_DbgScriptDirectiveKind_Compile) {
+ for MD_EachNode(sub_field, field->first) {
+ if (str8_matchi(sub_field->string, str8_lit("cc"))) {
+ MD_Node *cc = sub_field->first;
+ if (cc->flags & MD_NodeFlag_StringLiteral) {
+ if (str8_matchi(cc->string, str8_lit("clang"))) { dir->compile.cc = T_Compiler_Clang; }
+ else if (str8_matchi(cc->string, str8_lit("cl"))) { dir->compile.cc = T_Compiler_Cl; }
+ else {
+ t_errorf_md(file_path, source, cc, "unknown compiler name: \"%S\"\n", sub_field->string);
+ goto exit;
+ }
+ } else {
+ t_errorf_md(file_path, source, sub_field, "value of CC must be a string literal e.g. CC: \"clang\"\n");
+ goto exit;
+ }
+ } else if (str8_matchi(sub_field->string, str8_lit("args"))) {
+ MD_Node *args = sub_field->first;
+ if (args->flags & MD_NodeFlag_StringLiteral) {
+ dir->args = str8_copy(arena, args->string);
+ } else {
+ t_errorf_md(file_path, source, args, "value of ARGS must be a string literal\n");
+ goto exit;
+ }
+ } else {
+ t_errorf_md(file_path, source, sub_field, "unknown field \"%S\"\n", sub_field->string);
+ goto exit;
+ }
+ }
+ }
+ } else {
+ if (md_node_is_nil(field->first)) {
+ t_errorf_md(file_path, source, field, "missing value on field %S\n", field->string);
+ goto exit;
+ }
+ if ( ! md_node_is_nil(field->first->next)) {
+ t_errorf_md(file_path, source, field, "field %S accepts only one value\n", field->string);
+ goto exit;
+ }
+ if (~field->first->flags & MD_NodeFlag_StringLiteral) {
+ t_errorf_md(file_path, source, field, "field %S accepts only strings\n", field->string);
+ goto exit;
+ }
+ dir->args = str8_copy(arena, field->first->string);
+ }
+
T_DbgScriptDirectiveList *list = &script.directives[os][kind];
- SLLQueuePush(list->first, list->last, n);
+ SLLQueuePush(list->first, list->last, dir);
list->count += 1;
}
}
@@ -541,52 +589,59 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
// file
{
- MD_Node *last_file = 0;
+ MD_NodePtrList files = {0};
for MD_EachNode(n, script_parse.root->first->next) {
- B32 is_end = (md_node_is_nil(n->next) && last_file != 0);
- if (str8_matchi(n->string, str8_lit("file")) || is_end) {
- if (!is_end) {
- AssertAlways( ! md_node_is_nil(n->first));
- AssertAlways(md_node_is_nil(n->first->next));
+ if (str8_matchi(n->string, str8_lit("file"))) {
+ AssertAlways( ! md_node_is_nil(n->first));
+
+ if ( ! md_node_is_nil(n->first->next) || ! (n->first->flags & MD_NodeFlag_StringLiteral)) {
+ U32 line = 0;
+ for EachIndex(idx, n->first->src_offset) { line += (source.str[idx] == '\n'); }
+ t_errorf_md(file_path, source, n, "value of the 'file' must be a string, (e.g. file: \"main.c\")\n", file_path, line);
+ goto exit;
}
-
- if (last_file) {
- // src_offset -> base_line
- //
- // TODO: super silly!! mdesk should export line numbers
- U64 line = 1;
- for EachIndex(idx, last_file->src_offset) { line += (source.str[idx] == '\n'); }
-
- String8 sub_source = str8_substr(source, r1u64(last_file->src_offset, is_end ? source.size : n->src_offset));
- U64 file_dir_end = str8_find_needle(sub_source, 0, str8_lit("\n"), 0);
- U64 next_file_dir_begin = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), n->src_offset);
- sub_source = str8_substr(sub_source, r1u64(file_dir_end + 1, next_file_dir_begin));
-
- T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
- file->path = t_make_file_path(arena, last_file->first->string);
- file->source = sub_source;
- file->line = line;
- SLLQueuePush(script.files.first, script.files.last, file);
- script.files.count += 1;
- }
-
- last_file = n;
+
+ md_node_ptr_list_push(scratch.arena, &files, n);
}
}
-
- // no file directives? assume script file as main source file
- if (last_file == 0) {
+
+ if (files.count == 0) {
+ MD_Node *whole_file = push_array(scratch.arena, MD_Node, 1);
+ whole_file->first = push_array(scratch.arena, MD_Node, 1);
+ whole_file->first->next = push_array(scratch.arena, MD_Node, 1);
+ whole_file->src_offset = 0;
+ whole_file->first->string = str8_skip_last_slash(file_path);
+ md_node_ptr_list_push(scratch.arena, &files, whole_file);
+ }
+
+ HashMap files_hm = {0};
+ for EachNode(n_ptr, MD_NodePtrNode, files.first) {
+ MD_Node *n = n_ptr->v;
+
+ U64 src_opl = source.size;
+ if (n_ptr->next) {
+ src_opl = n_ptr->next->v->src_offset;
+ }
+
+ String8 sub_source = str8_substr(source, r1u64(n->src_offset, src_opl));
+ U64 file_min = str8_find_needle(sub_source, 0, str8_lit("\n"), 0) + 1;
+ U64 file_max = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), 0);
+ sub_source = str8_substr(sub_source, r1u64(file_min, file_max));
+
T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
- file->path = file_path;
- file->source = source;
+ file->path = t_make_file_path(arena, n->first->string);
+ file->source = sub_source;
+ t_find_line_and_col(source, n, &file->line, 0);
SLLQueuePush(script.files.first, script.files.last, file);
script.files.count += 1;
+
+ hash_map_push_raw_raw(scratch.arena, &files_hm, n, file);
}
}
// programs
{
- HashTable *ht = hash_table_init(scratch.arena, 256); //
+ HashMap hm = {0}; //
for MD_EachNode(n, script_parse.root->first->next) {
U64 order = 0;
if (try_u64_from_str8_c_rules(n->string, &order)) {
@@ -598,20 +653,14 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
}
AssertAlways(file != 0);
- // src_offset -> line
- //
- // TODO: super silly!! mdesk should export line numbers
- U64 line = 1;
- for EachIndex(i, n->src_offset) { line += (source.str[i] == '\n'); }
-
- T_DbgScriptProgram *p = hash_table_search_u64_raw(ht, order);
+ T_DbgScriptProgram *p = hash_map_search_u64_raw(&hm, order);
if (p == 0) {
p = push_array(arena, T_DbgScriptProgram, 1);
- p->line = line;
+ t_find_line_and_col(source, n, &p->line, 0);
p->order = order;
p->os = OperatingSystem_CURRENT;
p->file = file;
- hash_table_push_u64_raw(scratch.arena, ht, order, p);
+ hash_map_push_u64_raw(scratch.arena, &hm, order, p);
} else {
t_errorf("ERROR: duplicate order number %llu found on line %llu\n", (unsigned long long)order, (unsigned long long)p->line);
}
@@ -620,7 +669,7 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
// push new cmd
T_DbgScriptCmd *cmd = push_array(arena, T_DbgScriptCmd, 1);
cmd->kind = t_dbg_script_cmd_kind_from_string(cmd_n->string);
- cmd->line = line;
+ t_find_line_and_col(source, n, &cmd->line_num, &cmd->col_num);
Assert(cmd->kind != T_DbgScriptCmdKind_Null);
SLLQueuePush(p->first, p->last, cmd);
p->count += 1;
@@ -640,8 +689,8 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgS
}
}
- script.program_count = ht->count;
- script.programs = values_from_hash_table_raw(arena, ht);
+ script.program_count = hm.count;
+ script.programs = values_from_hash_map_raw(arena, &hm);
radsort(script.programs, script.program_count, t_dbg_script_program_is_before);
}
@@ -666,7 +715,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
if (program->os == OperatingSystem_CURRENT) {
for EachNode(cmd, T_DbgScriptCmd, program->first) {
- t_infof("[%llu] Command: %S:%llu %S\n", program->order, script->file_path, (unsigned long long)cmd->line, t_string_from_dbg_script_cmd_kind(cmd->kind));
+ t_infof("[%llu] Command: %S:%llu %S\n", program->order, script->file_path, (unsigned long long)cmd->line_num, t_string_from_dbg_script_cmd_kind(cmd->kind));
switch (cmd->kind) {
case T_DbgScriptCmdKind_Null: break;
@@ -694,7 +743,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
#else
T_DbgState *temp_status = t_dbg_state(scratch.arena, T_Dbg_DefaultTimeout);
if (temp_status == 0) {
- t_errorf("ERROR: %S:%llu: failed to query IP\n", script->file_path, (unsigned long long)cmd->line);
+ t_errorf("ERROR: %S:%llu: failed to query IP\n", script->file_path, (unsigned long long)cmd->line_num);
goto exit;
}
U64 ip = temp_status->ip;
@@ -703,7 +752,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
// map IP -> source location
T_DbgLineArray lines = {0};
if (t_dbg_src_line(scratch.arena, ip, &lines, T_Dbg_DefaultTimeout) == 0) {
- t_errorf("ERROR: %S:%llu: IP (0x%llx) does not map to a source line\n", script->file_path, (unsigned long long)cmd->line, (unsigned long long)ip);
+ t_errorf("ERROR: %S:%llu: IP (0x%llx) does not map to a source line\n", script->file_path, (unsigned long long)cmd->line_num, (unsigned long long)ip);
goto exit;
}
@@ -711,14 +760,18 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
S64 at_line_s64 = (S64)(program->line - program->file->line) + cmd->at.delta;
U64 at_line_u64 = at_line_s64 >= 0 ? (U64)at_line_s64 : 0;
AssertAlways(at_line_u64 > 0);
+
+ if (lines.count == 0) {
+ t_errorf("ERROR: %S:%llu:%llu: no source location maps for vaddr: 0x%llx\n", script->file_path, (unsigned long long)cmd->line_num, (unsigned long long)cmd->col_num, (unsigned long long)ip);
+ goto exit;
+ }
// match expected vs current debugger locations
-
for EachIndex(i, lines.count) {
B32 mismatch = lines.v[i].line_num != at_line_u64 ||
!str8_match(lines.v[i].file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
if (mismatch) {
- t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, (unsigned long long)cmd->line);
+ t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, (unsigned long long)cmd->line_num);
t_errorf(" Expected: %S:%llu\n", program->file->path, (unsigned long long)at_line_u64);
t_errorf(" Got : %S:%llu\n", lines.v[i].file_path, (unsigned long long)lines.v[i].line_num);
t_errorf(" IP : 0x%llx\n", (unsigned long long)ip);
@@ -758,6 +811,7 @@ T_RunSig(dbg_script_runner)
// source -> script
T_DbgScript script = {0};
if ( ! t_dbg_script_from_source(arena, user_data, source, &script)) {
+ result_out->status = T_RunStatus_Fail;
goto exit;
}
@@ -768,17 +822,23 @@ T_RunSig(dbg_script_runner)
T_Ok(0);
}
}
-
+
+ if (script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Skip].count) {
+ result_out->status = T_RunStatus_Skip;
+ goto exit;
+ }
+
// compiler vars
HashTable *script_vars = hash_table_init(arena, 1000);
+ hash_table_push_path_string(arena, script_vars, str8_lit("FILE"), user_data);
hash_table_push_path_string(arena, script_vars, str8_lit("CWD"), g_wdir);
// run compilers
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) {
- T_Compiler compiler = directive->compile.compiler;
-
+ T_Compiler compiler = directive->compile.cc;
+
// pick default compiler if none selected
- if (compiler == T_Compiler_Null) {
+ if (directive->compile.cc == T_Compiler_Null) {
switch (OperatingSystem_CURRENT) {
case OperatingSystem_Windows: { compiler = T_Compiler_Cl; } break;
case OperatingSystem_Linux: { compiler = T_Compiler_Clang; } break;
@@ -796,10 +856,16 @@ T_RunSig(dbg_script_runner)
// invoke compiler with arguments from directive
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
+
+ if (compiler == T_Compiler_Cl) { expanded_args = str8f(arena, "/nologo %S", expanded_args); }
+
if (t_invoke(compiler_path, expanded_args, max_U64) == 0) {
t_errorf("ERROR: failed to launch compiler: \"%S %S\"\n", compiler_path, expanded_args);
T_Ok(0);
}
+
+ if (compiler == T_Compiler_Cl) { g_output = str8_skip(g_output, str8_chop_line(&g_output).size); } // file name print
+
if (g_last_exit_code) {
t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->line, g_errors);
if (g_stop_on_first_fail_or_crash) {
@@ -875,7 +941,7 @@ t_dbg_register_script_tests(Arena *arena, String8 folder_path)
String8 file_name_escaped = str8_list_join(arena, &file_name_parts, &(StringJoin){.sep=str8_lit("-"), .post = str8_lit("\0") });
g_torture_tests_[g_torture_test_count++] = (T_Test){
- .group = T_Group,
+ .file = "raddbg",
.label = (char*)file_name_escaped.str,
.r = t_dbg_script_runner,
.user_data = str8_copy(arena, file_path),
@@ -885,4 +951,3 @@ t_dbg_register_script_tests(Arena *arena, String8 folder_path)
scratch_end(scratch);
}
-#undef T_Group
diff --git a/src/torture/torture_dbg.h b/src/torture/torture_dbg.h
index 1f22a318..39e73b05 100644
--- a/src/torture/torture_dbg.h
+++ b/src/torture/torture_dbg.h
@@ -21,31 +21,37 @@ typedef struct
T_DbgScriptFile *last;
} T_DbgScriptFileList;
+#define T_DbgScriptCmdKind_XList \
+ X(Breakpoint, "bp") \
+ X(ClearBreakpoints, "bp_clear") \
+ X(Run, "run") \
+ X(Halt, "halt") \
+ X(StepOver, "step_over") \
+ X(StepInto, "step_into") \
+ X(StepOut, "step_out") \
+ X(StepOverInst, "step_over_inst") \
+ X(StepIntoInst, "step_over_inst") \
+ X(StepOverLine, "step_over_line") \
+ X(StepIntoLine, "step_into_line") \
+ X(KillAll, "kll_all") \
+ X(At, "at") \
+ X(Eval, "eval")
+
typedef enum
{
T_DbgScriptCmdKind_Null,
- T_DbgScriptCmdKind_Breakpoint,
- T_DbgScriptCmdKind_ClearBreakpoints,
- T_DbgScriptCmdKind_Run,
- T_DbgScriptCmdKind_Halt,
- T_DbgScriptCmdKind_StepOver,
- T_DbgScriptCmdKind_StepInto,
- T_DbgScriptCmdKind_StepOut,
- T_DbgScriptCmdKind_StepOverInst,
- T_DbgScriptCmdKind_StepIntoInst,
- T_DbgScriptCmdKind_StepOverLine,
- T_DbgScriptCmdKind_StepIntoLine,
- T_DbgScriptCmdKind_KillAll,
- T_DbgScriptCmdKind_At,
- T_DbgScriptCmdKind_Eval,
+#define X(n,...) T_DbgScriptCmdKind_##n,
+ T_DbgScriptCmdKind_XList
+#undef X
T_DbgScriptCmdKind_Count
} T_DbgScriptCmdKind;
typedef struct T_DbgScriptCmd
{
struct T_DbgScriptCmd *next;
- T_DbgScriptCmdKind kind;
- U64 line;
+ T_DbgScriptCmdKind kind;
+ U64 line_num;
+ U64 col_num;
union {
struct {
S64 delta;
@@ -78,6 +84,7 @@ typedef enum
T_DbgScriptDirectiveKind_Compile,
T_DbgScriptDirectiveKind_Link,
T_DbgScriptDirectiveKind_Launch,
+ T_DbgScriptDirectiveKind_Skip,
T_DbgScriptDirectiveKind_Count,
} T_DbgScriptDirectiveKind;
@@ -89,7 +96,7 @@ typedef struct T_DbgScriptDirective
String8 args;
union {
struct {
- T_Compiler compiler;
+ T_Compiler cc;
} compile;
};
} T_DbgScriptDirective;
diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c
index 46d2d185..0649731a 100644
--- a/src/torture/torture_dwarf.c
+++ b/src/torture/torture_dwarf.c
@@ -1,8 +1,6 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
-#define T_Group "Dwarf"
-
internal U64
t_dw_test_uleb128(U64 v, U64 expected_length)
{
@@ -1040,4 +1038,3 @@ TEST(reg_split_spill)
}
#endif
-#undef T_Group
diff --git a/src/torture/torture_md.c b/src/torture/torture_md.c
index 7b87e7ed..5eabb69f 100644
--- a/src/torture/torture_md.c
+++ b/src/torture/torture_md.c
@@ -1,6 +1,6 @@
#define T_Group "MD"
-TEST(md_tokenizer)
+SKIP(md_tokenizer)
{
MD_TokenizeResult result;
diff --git a/src/torture/torture_p2r.c b/src/torture/torture_p2r.c
index a7804059..86ac00e1 100644
--- a/src/torture/torture_p2r.c
+++ b/src/torture/torture_p2r.c
@@ -1,5 +1,3 @@
-#define T_Group "p2r"
-
#if OS_WINDOWS
// TODO: location is not baked consistently
@@ -127,5 +125,3 @@ TEST(p2r_determ)
#endif
#endif
-#undef T_Group
-
diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c
index 56dcd802..f7ec015f 100644
--- a/src/torture/torture_radlink.c
+++ b/src/torture/torture_radlink.c
@@ -433,8 +433,6 @@ t_write_entry_obj(void)
////////////////////////////////
-#define T_Group "Linker"
-
TEST(machine_compat_check)
{
// unknown.obj
@@ -4712,7 +4710,7 @@ TEST(relocate_undefined_section_symbol)
T_Ok(t_write_def_obj(obj_name, (T_COFF_DefObj){
.machine = T_COFF_DefSetMachine(X64),
.sections = (T_COFF_DefSection[]){
- { target_name, sect_name, target_name, .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT },
+ { target_name, sect_name, str8_cstring(target_name), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT },
{0}
},
.symbols = (T_COFF_DefSymbol[]){
@@ -7203,4 +7201,3 @@ TEST(lib_member_reloc_apply_off_out_of_bounds)
}
#endif
-#undef T_Group