mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-10-15 21:39:41 -07:00
print out last captured output when a test fails or crashes
This commit is contained in:
1 parent
a645622b10
commit
887ee21d94
7 files changed
+245
-214
No files matched your search
+110
-72
@@ -2,21 +2,23 @@
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
// command line
|
||||
global String8 g_stdout_file_name = str8_lit_comp("torture.out");
|
||||
global String8 g_wdir;
|
||||
global String8 g_out = str8_lit_comp("torture");
|
||||
global B32 g_verbose;
|
||||
global B32 g_redirect_stdout = 1;
|
||||
global B32 g_stop_on_first_fail_or_crash = 1;
|
||||
global String8 g_linker;
|
||||
global String8 g_test_data;
|
||||
global String8 g_stdout_file_name = str8_lit_comp("torture.out");
|
||||
global String8 g_wdir;
|
||||
global String8 g_out = str8_lit_comp("torture");
|
||||
global B32 g_verbose;
|
||||
global B32 g_redirect_stdout = 1;
|
||||
global B32 g_stop_on_first_fail_or_crash = 1;
|
||||
global String8 g_linker;
|
||||
global String8 g_test_data;
|
||||
|
||||
// tests
|
||||
U64 g_torture_test_count;
|
||||
T_Test g_torture_tests[0xffffff];
|
||||
|
||||
// invoke
|
||||
global int g_last_exit_code;
|
||||
global int g_last_exit_code;
|
||||
global Arena *g_output_arena;
|
||||
global String8 g_output;
|
||||
|
||||
internal String8
|
||||
t_test_name_from_idx(Arena *arena, U64 test_idx)
|
||||
@@ -180,6 +182,11 @@ t_run(T_Run run, String8 user_data)
|
||||
} else {
|
||||
t_run_caller(&ctx);
|
||||
}
|
||||
|
||||
if (ctx.result.status == T_RunStatus_Fail || ctx.result.status == T_RunStatus_Crash) {
|
||||
fprintf(stderr, "Last captured output:\n%.*s\n", str8_varg(g_output));
|
||||
}
|
||||
|
||||
return ctx.result;
|
||||
}
|
||||
|
||||
@@ -228,27 +235,27 @@ t_cl_version(void)
|
||||
|
||||
if ( ! version.size) {
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_cl_path(), str8_zero(), max_U64, scratch.arena, &output);
|
||||
|
||||
t_invoke_cl("");
|
||||
AssertAlways(g_last_exit_code == 0);
|
||||
|
||||
String8 needle = str8_lit("Version");
|
||||
U64 version_lo = str8_find_needle(output, 0, needle, 0);
|
||||
U64 version_lo = str8_find_needle(g_output, 0, needle, 0);
|
||||
version_lo += needle.size + 1;
|
||||
AssertAlways(version_lo < output.size);
|
||||
|
||||
U64 version_hi = str8_find_needle(output, version_lo, str8_lit(" "), 0);
|
||||
AssertAlways(version_hi < output.size);
|
||||
|
||||
version = str8_substr(output, r1u64(version_lo, version_hi));
|
||||
AssertAlways(version_lo < g_output.size);
|
||||
|
||||
U64 version_hi = str8_find_needle(g_output, version_lo, str8_lit(" "), 0);
|
||||
AssertAlways(version_hi < g_output.size);
|
||||
|
||||
version = str8_substr(g_output, r1u64(version_lo, version_hi));
|
||||
AssertAlways(version.size > 0);
|
||||
|
||||
local_persist U8 buffer[4096];
|
||||
ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer };
|
||||
Arena *arena = arena_alloc_(¶ms);
|
||||
version = str8_copy(arena, version);
|
||||
|
||||
|
||||
MemoryZeroStruct(&g_output);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
@@ -322,27 +329,27 @@ t_src_path(void)
|
||||
}
|
||||
|
||||
internal B32
|
||||
t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout, Arena *output_arena, String8 *output_out)
|
||||
t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout)
|
||||
{
|
||||
Temp scratch = scratch_begin(&output_arena,1);
|
||||
|
||||
Temp scratch = scratch_begin(&g_output_arena,1);
|
||||
|
||||
B32 is_ok = 0;
|
||||
|
||||
B32 capture_output = output_out || g_redirect_stdout;
|
||||
|
||||
|
||||
// clean up global state
|
||||
arena_pop_to(g_output_arena, 0);
|
||||
MemoryZeroStruct(&g_output);
|
||||
g_last_exit_code = -1;
|
||||
|
||||
|
||||
#if OS_WINDOWS
|
||||
File read_pipe_handle = {0}, write_pipe_handle = {0};
|
||||
if (capture_output) {
|
||||
HANDLE read_pipe, write_pipe;
|
||||
SECURITY_ATTRIBUTES at = { .nLength = sizeof(at), .bInheritHandle = 1 };
|
||||
if (!CreatePipe(&read_pipe, &write_pipe, &at, 0)) {
|
||||
AssertAlways(0 && "failed to create a pipe");
|
||||
}
|
||||
read_pipe_handle = (File){ .u64[0] = (U64)read_pipe };
|
||||
write_pipe_handle = (File){ .u64[0] = (U64)write_pipe };
|
||||
}
|
||||
|
||||
HANDLE read_pipe, write_pipe;
|
||||
SECURITY_ATTRIBUTES at = { .nLength = sizeof(at), .bInheritHandle = 1 };
|
||||
if (!CreatePipe(&read_pipe, &write_pipe, &at, 0)) { AssertAlways(0 && "failed to create a pipe"); }
|
||||
read_pipe_handle = (File){ .u64[0] = (U64)read_pipe };
|
||||
write_pipe_handle = (File){ .u64[0] = (U64)write_pipe };
|
||||
#else
|
||||
# error NotImplemented
|
||||
#endif
|
||||
// Build Launch Options
|
||||
ProcessLaunchParams launch_opts = {
|
||||
.path = g_wdir,
|
||||
@@ -362,54 +369,84 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout, Ar
|
||||
|
||||
// invoke exe
|
||||
Process process_handle = process_launch(&launch_opts);
|
||||
if (process_match(process_handle, process_zero())) { goto exit; }
|
||||
|
||||
// close handle so last to ReadFile does not block
|
||||
file_close(write_pipe_handle);
|
||||
|
||||
if ( ! process_match(process_handle, process_zero())) {
|
||||
if (capture_output) {
|
||||
// capture process output
|
||||
String8List output = {0};
|
||||
for (;;) {
|
||||
String8 string = file_read_cstring(scratch.arena, read_pipe_handle, 0);
|
||||
if (string.size == 0) { break; }
|
||||
str8_list_push(scratch.arena, &output, string);
|
||||
}
|
||||
file_close(read_pipe_handle);
|
||||
|
||||
if (output_out) {
|
||||
*output_out = str8_list_join(output_arena, &output, 0);
|
||||
}
|
||||
|
||||
// write to the output file
|
||||
if (g_redirect_stdout) {
|
||||
write_data_list_to_file_path(g_stdout_file_name, output);
|
||||
}
|
||||
}
|
||||
|
||||
U64 exit_code_u64 = 0;
|
||||
if (process_join(process_handle, timeout, &exit_code_u64)) {
|
||||
g_last_exit_code = (int)exit_code_u64;
|
||||
is_ok = 1;
|
||||
} else {
|
||||
process_kill(process_handle);
|
||||
}
|
||||
// capture process output
|
||||
String8List output = {0};
|
||||
for (;;) {
|
||||
String8 string = file_read_cstring(scratch.arena, read_pipe_handle, 0);
|
||||
if (string.size == 0) { break; }
|
||||
str8_list_push(scratch.arena, &output, string);
|
||||
}
|
||||
file_close(read_pipe_handle);
|
||||
|
||||
// update output global
|
||||
g_output = str8_list_join(g_output_arena, &output, 0);
|
||||
|
||||
// write to the output file
|
||||
if (g_redirect_stdout) {
|
||||
write_data_list_to_file_path(g_stdout_file_name, output);
|
||||
}
|
||||
|
||||
U64 exit_code_u64 = 0;
|
||||
if (process_join(process_handle, timeout, &exit_code_u64)) {
|
||||
g_last_exit_code = (int)exit_code_u64;
|
||||
is_ok = 1;
|
||||
} else {
|
||||
process_kill(process_handle);
|
||||
}
|
||||
|
||||
exit:;
|
||||
scratch_end(scratch);
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
internal B32
|
||||
t_invoke_(String8 exe_path, String8 cmdline, U64 timeout, Arena *output_arena, String8 *output_out)
|
||||
t_invoke(String8 exe_path, String8 cmdline, U64 timeout)
|
||||
{
|
||||
return t_invoke_env(exe_path, cmdline, (String8List){0}, timeout, output_arena, output_out);
|
||||
return t_invoke_env(exe_path, cmdline, (String8List){0}, timeout);
|
||||
}
|
||||
|
||||
internal B32
|
||||
t_invoke(String8 exe_path, String8 cmdline, U64 timeout)
|
||||
t_invoke_cl(char *fmt, ...)
|
||||
{
|
||||
return t_invoke_(exe_path, cmdline, timeout, 0, 0);
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
|
||||
va_end(args);
|
||||
B32 is_ok = t_invoke(t_cl_path(), cmdl, max_U64);
|
||||
scratch_end(scratch);
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
internal B32
|
||||
t_invoke_linkerf(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
|
||||
va_end(args);
|
||||
B32 is_ok = t_invoke(t_radlink_path(), cmdl, max_U64);
|
||||
scratch_end(scratch);
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
internal B32
|
||||
t_invoke_radbin(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
|
||||
va_end(args);
|
||||
B32 is_ok = t_invoke(t_radbin_path(), cmdl, max_U64);
|
||||
scratch_end(scratch);
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
internal void
|
||||
@@ -422,10 +459,10 @@ t_kill_all(String8 pattern)
|
||||
while (dmn_process_iter_next(scratch.arena, &it, &info)) {
|
||||
if (str8_match_wildcard(info.name, pattern, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) {
|
||||
#if OS_WINDOWS
|
||||
if (!t_invoke_(str8_lit("taskkill"), str8f(scratch.arena, "/PID %u /F", info.pid), max_U64, 0, 0)) { fprintf(stderr, "ERROR: failed to invoke taskkill\n"); }
|
||||
if (!t_invoke(str8_lit("taskkill"), str8f(scratch.arena, "/PID %u /F", info.pid), max_U64)) { fprintf(stderr, "ERROR: failed to invoke taskkill\n"); }
|
||||
#elif OS_LINUX
|
||||
NotImplemented; // TODO: test
|
||||
if (!t_invoke_(str8_lit("kill"), str8f(scratch.arena, " -9 %u", info.pid), max_U64, 0, 0)) { fprintf(stderr, "ERROR: failed to invoke kill\n"); }
|
||||
if (!t_invoke(str8_lit("kill"), str8f(scratch.arena, " -9 %u", info.pid), max_U64)) { fprintf(stderr, "ERROR: failed to invoke kill\n"); }
|
||||
#else
|
||||
# error NotImplemented
|
||||
#endif
|
||||
@@ -703,7 +740,8 @@ t_entry_point(CmdLine *cmdline)
|
||||
g_verbose = cmd_line_has_flag(cmdline, str8_lit("verbose"));
|
||||
g_redirect_stdout = !cmd_line_has_flag(cmdline, str8_lit("print_stdout"));
|
||||
g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going"));
|
||||
|
||||
g_output_arena = arena_alloc();
|
||||
|
||||
// default options when running under debugger
|
||||
#if OS_WINDOWS
|
||||
if (!cmd_line_has_flag(cmdline, str8_lit("print_stdout")) && IsDebuggerPresent()) {
|
||||
|
||||
Reference in new issue
Block a user