diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a22e801dc..152a47edfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ set(C_HEADERS set(ZIG_STD_SRC "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" "${CMAKE_SOURCE_DIR}/std/builtin.zig" + "${CMAKE_SOURCE_DIR}/std/test_runner.zig" "${CMAKE_SOURCE_DIR}/std/std.zig" "${CMAKE_SOURCE_DIR}/std/syscall.zig" "${CMAKE_SOURCE_DIR}/std/errno.zig" diff --git a/src/all_types.hpp b/src/all_types.hpp index 76cf8749f4..cb0406b326 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -36,11 +36,6 @@ enum OutType { OutTypeObj, }; -enum CodeGenBuildType { - CodeGenBuildTypeDebug, - CodeGenBuildTypeRelease, -}; - struct ConstEnumValue { uint64_t tag; ConstExprValue *payload; @@ -983,6 +978,7 @@ struct FnTableEntry { bool is_inline; bool internal_linkage; bool is_extern; + bool is_test; uint32_t ref_count; // if this is 0 we don't have to codegen it // reminder: hash tables must be initialized before use @@ -1073,7 +1069,8 @@ struct CodeGen { bool link_libc; Buf *libc_lib_dir; Buf *libc_include_dir; - CodeGenBuildType build_type; + bool is_release_build; + bool is_test_build; LLVMTargetMachineRef target_machine; LLVMZigDIFile *dummy_di_file; bool is_native_target; @@ -1087,10 +1084,11 @@ struct CodeGen { // there will not be a corresponding fn_defs entry. ZigList fn_protos; ZigList global_vars; - ZigList global_const_list; + ZigList global_const_list; OutType out_type; FnTableEntry *cur_fn; + FnTableEntry *main_fn; LLVMValueRef cur_ret_ptr; ZigList break_block_stack; ZigList continue_block_stack; @@ -1103,6 +1101,7 @@ struct CodeGen { ErrColor err_color; ImportTableEntry *root_import; ImportTableEntry *bootstrap_import; + ImportTableEntry *test_runner_import; LLVMValueRef memcpy_fn_val; LLVMValueRef memset_fn_val; LLVMValueRef trap_fn_val; @@ -1116,6 +1115,8 @@ struct CodeGen { const char **clang_argv; int clang_argv_len; ZigList lib_dirs; + + uint32_t test_fn_count; }; struct VariableTableEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index 79d472f033..1821fbc914 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -781,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t bool is_cold = false; bool is_naked = false; + bool is_test = false; if (fn_proto->directives) { for (int i = 0; i < fn_proto->directives->length; i += 1) { @@ -794,6 +795,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t is_naked = true; } else if (buf_eql_str(attr_name, "cold")) { is_cold = true; + } else if (buf_eql_str(attr_name, "test")) { + is_test = true; + g->test_fn_count += 1; } else { add_node_error(g, directive_node, buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); @@ -813,6 +817,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t is_naked, is_cold); fn_table_entry->type_entry = fn_type; + fn_table_entry->is_test = is_test; if (fn_type->id == TypeTableEntryIdInvalid) { fn_proto->skip = true; @@ -846,7 +851,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t unsigned scope_line = line_number; bool is_definition = fn_table_entry->fn_def_node != nullptr; unsigned flags = 0; - bool is_optimized = g->build_type == CodeGenBuildTypeRelease; + bool is_optimized = g->is_release_build; LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", import->di_file, line_number, @@ -1247,10 +1252,18 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, fn_table->put(proto_name, fn_table_entry); - if (!struct_type && - g->bootstrap_import && - import == g->root_import && buf_eql_str(proto_name, "main")) - { + bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main"); + if (is_main_fn) { + g->main_fn = fn_table_entry; + + if (g->bootstrap_import && !g->is_test_build) { + g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); + } + } + bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); + if (is_test_main_fn) { + assert(g->bootstrap_import); + assert(g->is_test_build); g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); } @@ -1551,13 +1564,14 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) { } } -static void add_global_const_expr(CodeGen *g, Expr *expr) { +static void add_global_const_expr(CodeGen *g, AstNode *expr_node) { + Expr *expr = get_resolved_expr(expr_node); if (expr->const_val.ok && type_has_codegen_value(expr->type_entry) && !expr->has_global_const && type_has_bits(expr->type_entry)) { - g->global_const_list.append(expr); + g->global_const_list.append(expr_node); expr->has_global_const = true; } } @@ -1924,7 +1938,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn *child_node, expected_type, child_types[i]); Expr *expr = get_resolved_expr(*child_node); expr->type_entry = resolved_type; - add_global_const_expr(g, expr); + add_global_const_expr(g, *child_node); } return expected_type; @@ -4022,7 +4036,9 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry if (buf_eql_str(&var_name, "is_big_endian")) { return resolve_expr_const_val_as_bool(g, node, g->is_big_endian); } else if (buf_eql_str(&var_name, "is_release")) { - return resolve_expr_const_val_as_bool(g, node, g->build_type == CodeGenBuildTypeRelease); + return resolve_expr_const_val_as_bool(g, node, g->is_release_build); + } else if (buf_eql_str(&var_name, "is_test")) { + return resolve_expr_const_val_as_bool(g, node, g->is_test_build); } else { add_node_error(g, *str_node, buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); @@ -4752,7 +4768,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, expr->type_entry = return_type; node->block_context = context; - add_global_const_expr(g, expr); + add_global_const_expr(g, node); return resolved_type; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 33c6b2b53c..c681cce90f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -28,7 +28,8 @@ CodeGen *codegen_create(Buf *root_source_dir) { g->primitive_type_table.init(32); g->unresolved_top_level_decls.init(32); g->fn_type_table.init(32); - g->build_type = CodeGenBuildTypeDebug; + g->is_release_build = false; + g->is_test_build = false; g->root_source_dir = root_source_dir; g->next_error_index = 1; g->error_value_count = 1; @@ -44,8 +45,12 @@ void codegen_set_clang_argv(CodeGen *g, const char **args, int len) { g->clang_argv_len = len; } -void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) { - g->build_type = build_type; +void codegen_set_is_release(CodeGen *g, bool is_release_build) { + g->is_release_build = is_release_build; +} + +void codegen_set_is_test(CodeGen *g, bool is_test_build) { + g->is_test_build = is_test_build; } void codegen_set_is_static(CodeGen *g, bool is_static) { @@ -1003,7 +1008,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { assert(expr_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *child_type = expr_type->data.error.child_type; - if (g->build_type != CodeGenBuildTypeRelease) { + if (!g->is_release_build) { LLVMValueRef err_val; if (type_has_bits(child_type)) { add_debug_source_node(g, node); @@ -1044,7 +1049,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { assert(expr_type->id == TypeTableEntryIdMaybe); TypeTableEntry *child_type = expr_type->data.maybe.child_type; - if (g->build_type != CodeGenBuildTypeRelease) { + if (!g->is_release_build) { add_debug_source_node(g, node); LLVMValueRef cond_val; if (child_type->id == TypeTableEntryIdPointer || @@ -1977,7 +1982,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { } else if (type_entry->id == TypeTableEntryIdUnreachable) { assert(node->data.container_init_expr.entries.length == 0); add_debug_source_node(g, node); - if (g->build_type != CodeGenBuildTypeRelease) { + if (!g->is_release_build) { LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); } return LLVMBuildUnreachable(g->builder); @@ -2233,7 +2238,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa } } } - if (!ignore_uninit && g->build_type != CodeGenBuildTypeRelease) { + if (!ignore_uninit && !g->is_release_build) { TypeTableEntry *isize = g->builtin_types.entry_isize; uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); uint64_t align_bytes = get_memcpy_align(g, variable->type); @@ -2645,7 +2650,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE static void gen_const_globals(CodeGen *g) { for (int i = 0; i < g->global_const_list.length; i += 1) { - Expr *expr = g->global_const_list.at(i); + AstNode *expr_node = g->global_const_list.at(i); + Expr *expr = get_resolved_expr(expr_node); ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); TypeTableEntry *type_entry = expr->type_entry; @@ -2680,6 +2686,24 @@ static void delete_unused_builtin_fns(CodeGen *g) { } } +static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) { + if (g->is_test_build) { + if (fn_entry->is_test) { + return false; + } + if (fn_entry == g->main_fn) { + return true; + } + return fn_entry->ref_count == 0; + } + + if (fn_entry->is_test) { + return true; + } + + return fn_entry->ref_count == 0; +} + static void do_code_gen(CodeGen *g) { assert(!g->errors.length); @@ -2731,12 +2755,19 @@ static void do_code_gen(CodeGen *g) { var->value_ref = global_value; } + LLVMValueRef *test_fn_vals = nullptr; + uint32_t next_test_index = 0; + if (g->is_test_build) { + test_fn_vals = allocate(g->test_fn_count); + } + // Generate function prototypes for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) { FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i); - if (fn_table_entry->ref_count == 0) { + if (skip_fn_codegen(g, fn_table_entry)) { // huge time saver LLVMDeleteFunction(fn_table_entry->fn_value); + fn_table_entry->fn_value = nullptr; continue; } @@ -2785,12 +2816,44 @@ static void do_code_gen(CodeGen *g) { } } + if (fn_table_entry->is_test) { + test_fn_vals[next_test_index] = fn_table_entry->fn_value; + next_test_index += 1; + } + } + + // Generate the list of test function pointers. + if (g->is_test_build) { + assert(g->test_fn_count > 0); + assert(next_test_index == g->test_fn_count); + + { + LLVMValueRef test_fn_array_val = LLVMConstArray(LLVMTypeOf(test_fn_vals[0]), + test_fn_vals, g->test_fn_count); + LLVMValueRef global_value = LLVMAddGlobal(g->module, + LLVMTypeOf(test_fn_array_val), "zig_test_fn_list"); + LLVMSetInitializer(global_value, test_fn_array_val); + LLVMSetLinkage(global_value, LLVMExternalLinkage); + LLVMSetGlobalConstant(global_value, true); + LLVMSetUnnamedAddr(global_value, true); + } + + { + LLVMValueRef test_fn_count_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, + g->test_fn_count, false); + LLVMValueRef global_value = LLVMAddGlobal(g->module, + LLVMTypeOf(test_fn_count_val), "zig_test_fn_count"); + LLVMSetInitializer(global_value, test_fn_count_val); + LLVMSetLinkage(global_value, LLVMExternalLinkage); + LLVMSetGlobalConstant(global_value, true); + LLVMSetUnnamedAddr(global_value, true); + } } // Generate function definitions. for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i); - if (fn_table_entry->ref_count == 0) { + if (skip_fn_codegen(g, fn_table_entry)) { // huge time saver continue; } @@ -3297,8 +3360,7 @@ static void init(CodeGen *g, Buf *source_path) { char *native_cpu = LLVMZigGetHostCPUName(); char *native_features = LLVMZigGetNativeFeatures(); - LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ? - LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive; + LLVMCodeGenOptLevel opt_level = g->is_release_build ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; @@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) { Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); - bool is_optimized = g->build_type == CodeGenBuildTypeRelease; + bool is_optimized = g->is_release_build; const char *flags = ""; unsigned runtime_version = 0; g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(), @@ -3611,14 +3673,10 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou buf_sprintf("missing export declaration and export type not provided")); } - if (!g->link_libc) { + if (!g->link_libc && !g->is_test_build) { if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) { g->bootstrap_import = add_special_code(g, "bootstrap.zig"); } - - if (g->out_type == OutTypeExe) { - add_special_code(g, "builtin.zig"); - } } if (g->verbose) { @@ -3677,6 +3735,8 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) { } static void generate_h_file(CodeGen *g) { + assert(!g->is_test_build); + Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name)); FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb"); if (!out_h) @@ -3768,8 +3828,38 @@ static const char *get_libc_file(CodeGen *g, const char *file) { return buf_ptr(out_buf); } +static Buf *build_o(CodeGen *parent_gen, const char *oname) { + Buf *source_basename = buf_sprintf("%s.zig", oname); + Buf *std_dir_path = buf_create_from_str(ZIG_STD_DIR); + + CodeGen *child_gen = codegen_create(std_dir_path); + codegen_set_is_release(child_gen, parent_gen->is_release_build); + + codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); + codegen_set_is_static(child_gen, parent_gen->is_static); + + codegen_set_out_type(child_gen, OutTypeObj); + codegen_set_out_name(child_gen, buf_create_from_str(oname)); + + codegen_set_verbose(child_gen, parent_gen->verbose); + codegen_set_errmsg_color(child_gen, parent_gen->err_color); + + Buf *full_path = buf_alloc(); + os_path_join(std_dir_path, source_basename, full_path); + Buf source_code = BUF_INIT; + if (os_fetch_file_path(full_path, &source_code)) { + zig_panic("unable to fetch file: %s\n", buf_ptr(full_path)); + } + + codegen_add_root_code(child_gen, std_dir_path, source_basename, &source_code); + Buf *o_out = buf_sprintf("%s.o", oname); + codegen_link(child_gen, buf_ptr(o_out)); + + return o_out; +} + void codegen_link(CodeGen *g, const char *out_file) { - bool is_optimized = (g->build_type == CodeGenBuildTypeRelease); + bool is_optimized = g->is_release_build; if (is_optimized) { if (g->verbose) { fprintf(stderr, "\nOptimization:\n"); @@ -3788,6 +3878,7 @@ void codegen_link(CodeGen *g, const char *out_file) { } if (!out_file) { + assert(g->root_out_name); out_file = buf_ptr(g->root_out_name); } @@ -3821,6 +3912,7 @@ void codegen_link(CodeGen *g, const char *out_file) { return; } + // invoke `ld` ZigList args = {0}; const char *crt1o; @@ -3871,6 +3963,16 @@ void codegen_link(CodeGen *g, const char *out_file) { args.append(get_libc_file(g, "crtn.o")); } + if (g->is_test_build) { + Buf *test_runner_o_path = build_o(g, "test_runner"); + args.append(buf_ptr(test_runner_o_path)); + } + + if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { + Buf *builtin_o_path = build_o(g, "builtin"); + args.append(buf_ptr(builtin_o_path)); + } + for (int i = 0; i < g->lib_dirs.length; i += 1) { const char *lib_dir = g->lib_dirs.at(i); args.append("-L"); diff --git a/src/codegen.hpp b/src/codegen.hpp index 2682204efe..b1bd981939 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -16,7 +16,9 @@ CodeGen *codegen_create(Buf *root_source_dir); void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len); -void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type); +void codegen_set_is_release(CodeGen *codegen, bool is_release); +void codegen_set_is_test(CodeGen *codegen, bool is_test); + void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); void codegen_set_verbose(CodeGen *codegen, bool verbose); diff --git a/src/main.cpp b/src/main.cpp index 1b671007a2..7f288ba4ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ static int usage(const char *arg0) { fprintf(stderr, "Usage: %s [command] [options]\n" "Commands:\n" " build create executable, object, or library from target\n" + " test create and run a test build\n" " version print version number and exit\n" " parseh convert a c header file to zig extern declarations\n" "Options:\n" @@ -40,6 +41,7 @@ static int usage(const char *arg0) { enum Cmd { CmdInvalid, CmdBuild, + CmdTest, CmdVersion, CmdParseH, }; @@ -49,7 +51,7 @@ int main(int argc, char **argv) { Cmd cmd = CmdInvalid; const char *in_file = nullptr; const char *out_file = nullptr; - bool release = false; + bool is_release_build = false; bool strip = false; bool is_static = false; OutType out_type = OutTypeUnknown; @@ -67,7 +69,7 @@ int main(int argc, char **argv) { if (arg[0] == '-') { if (strcmp(arg, "--release") == 0) { - release = true; + is_release_build = true; } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { @@ -127,6 +129,8 @@ int main(int argc, char **argv) { cmd = CmdVersion; } else if (strcmp(arg, "parseh") == 0) { cmd = CmdParseH; + } else if (strcmp(arg, "test") == 0) { + cmd = CmdTest; } else { fprintf(stderr, "Unrecognized command: %s\n", arg); return usage(arg0); @@ -135,6 +139,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuild: case CmdParseH: + case CmdTest: if (!in_file) { in_file = arg; } else { @@ -152,6 +157,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuild: case CmdParseH: + case CmdTest: { if (!in_file) return usage(arg0); @@ -178,14 +184,22 @@ int main(int argc, char **argv) { } CodeGen *g = codegen_create(&root_source_dir); - codegen_set_build_type(g, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); + codegen_set_is_release(g, is_release_build); + codegen_set_is_test(g, cmd == CmdTest); + codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); codegen_set_strip(g, strip); codegen_set_is_static(g, is_static); - if (out_type != OutTypeUnknown) + if (out_type != OutTypeUnknown) { codegen_set_out_type(g, out_type); - if (out_name) + } else if (cmd == CmdTest) { + codegen_set_out_type(g, OutTypeExe); + } + if (out_name) { codegen_set_out_name(g, buf_create_from_str(out_name)); + } else if (cmd == CmdTest) { + codegen_set_out_name(g, buf_create_from_str("test")); + } if (libc_lib_dir) codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir)); if (libc_include_dir) @@ -205,6 +219,17 @@ int main(int argc, char **argv) { codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code); codegen_render_ast(g, stdout, 4); return EXIT_SUCCESS; + } else if (cmd == CmdTest) { + codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); + codegen_link(g, "./test"); + ZigList args = {0}; + int return_code; + os_spawn_process("./test", args, &return_code); + if (return_code != 0) { + fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); + fprintf(stderr, "./test\n"); + } + return return_code; } else { zig_unreachable(); } diff --git a/src/os.cpp b/src/os.cpp index fd87cef082..5735878f02 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -17,25 +17,24 @@ #include #include -void os_spawn_process(const char *exe, ZigList &args, bool detached) { +void os_spawn_process(const char *exe, ZigList &args, int *return_code) { pid_t pid = fork(); if (pid == -1) zig_panic("fork failed"); - if (pid != 0) - return; - if (detached) { - if (setsid() == -1) - zig_panic("process detach failed"); + if (pid == 0) { + // child + const char **argv = allocate(args.length + 2); + argv[0] = exe; + argv[args.length + 1] = nullptr; + for (int i = 0; i < args.length; i += 1) { + argv[i + 1] = args.at(i); + } + execvp(exe, const_cast(argv)); + zig_panic("execvp failed: %s", strerror(errno)); + } else { + // parent + waitpid(pid, return_code, 0); } - - const char **argv = allocate(args.length + 2); - argv[0] = exe; - argv[args.length + 1] = nullptr; - for (int i = 0; i < args.length; i += 1) { - argv[i + 1] = args.at(i); - } - execvp(exe, const_cast(argv)); - zig_panic("execvp failed: %s", strerror(errno)); } static int read_all_fd_stream(int fd, Buf *out_buf) { diff --git a/src/os.hpp b/src/os.hpp index 172059bda2..b96ba9b0ef 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -13,7 +13,7 @@ #include -void os_spawn_process(const char *exe, ZigList &args, bool detached); +void os_spawn_process(const char *exe, ZigList &args, int *return_code); void os_exec_process(const char *exe, ZigList &args, int *return_code, Buf *out_stderr, Buf *out_stdout); diff --git a/std/test_runner.zig b/std/test_runner.zig new file mode 100644 index 0000000000..e1a55b1388 --- /dev/null +++ b/std/test_runner.zig @@ -0,0 +1,45 @@ +import "std.zig"; + +/* +struct TestFn { + name: []u8, + func: extern fn(), +} + +extern var test_fn_list: []TestFn; +*/ + +extern var zig_test_fn_count: isize; + +// TODO make this a slice of structs +extern var zig_test_fn_list: [99999999]extern fn(); + +pub fn main(args: [][]u8) -> %void { + var i : isize = 0; + while (i < zig_test_fn_count) { + %%stderr.print_str("Test "); + // TODO get rid of the isize + %%stderr.print_i64(i + isize(1)); + %%stderr.print_str("/"); + %%stderr.print_i64(zig_test_fn_count); + %%stderr.print_str(" "); + /* + %%stderr.print_str(test_fn.name); + */ + %%stderr.print_str("..."); + +/* + // TODO support calling function pointers as fields directly + const fn_ptr = test_fn.func; + fn_ptr(); + */ + + const test_fn = zig_test_fn_list[i]; + test_fn(); + + %%stderr.print_str("OK\n"); + %%stderr.flush(); + + i += 1; + } +}