mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-25 07:04:58 -07:00
Begin rudimentary work on implementing odin test tooling with *_test.odin files
This commit is contained in:
+16
-2
@@ -111,9 +111,10 @@ enum CommandKind : u32 {
|
||||
Command_query = 1<<4,
|
||||
Command_doc = 1<<5,
|
||||
Command_version = 1<<6,
|
||||
Command_test = 1<<7,
|
||||
|
||||
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc,
|
||||
Command__does_build = Command_run|Command_build,
|
||||
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
|
||||
Command__does_build = Command_run|Command_build|Command_test,
|
||||
Command_all = ~(u32)0,
|
||||
};
|
||||
|
||||
@@ -333,6 +334,19 @@ bool is_excluded_target_filename(String name) {
|
||||
String original_name = name;
|
||||
name = remove_extension_from_path(name);
|
||||
|
||||
if (string_starts_with(name, str_lit("."))) {
|
||||
// Ignore .*.odin files
|
||||
return true;
|
||||
}
|
||||
|
||||
String test_suffix = str_lit("_test");
|
||||
if (build_context.command_kind != Command_test) {
|
||||
if (string_ends_with(name, test_suffix) && name != test_suffix) {
|
||||
// Ignore *_test.odin files
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
String str1 = {};
|
||||
String str2 = {};
|
||||
isize n = 0;
|
||||
|
||||
+48
-2
@@ -839,6 +839,8 @@ void init_checker_info(CheckerInfo *i) {
|
||||
array_init(&i->variable_init_order, a);
|
||||
array_init(&i->required_foreign_imports_through_force, a);
|
||||
array_init(&i->required_global_variables, a);
|
||||
array_init(&i->testing_procedures, a, 0, 0);
|
||||
|
||||
|
||||
i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
|
||||
if (i->allow_identifier_uses) {
|
||||
@@ -1854,7 +1856,50 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||
}
|
||||
}
|
||||
|
||||
add_dependency_to_set(c, start);
|
||||
if (build_context.command_kind == Command_test) {
|
||||
AstPackage *pkg = c->info.init_package;
|
||||
Scope *s = pkg->scope;
|
||||
for_array(i, s->elements.entries) {
|
||||
Entity *e = s->elements.entries[i].value;
|
||||
if (e->kind != Entity_Procedure) {
|
||||
continue;
|
||||
}
|
||||
String name = e->token.string;
|
||||
String prefix = str_lit("test_");
|
||||
|
||||
|
||||
if (!string_starts_with(name, prefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_tester = false;
|
||||
if (name != prefix) {
|
||||
is_tester = true;
|
||||
} else {
|
||||
if (e->file && e->file->is_test) {
|
||||
error(e->token, "Invalid testing procedure name: %.*s", LIT(name));
|
||||
}
|
||||
}
|
||||
|
||||
Type *t = base_type(e->type);
|
||||
GB_ASSERT(t->kind == Type_Proc);
|
||||
if (t->Proc.param_count == 0 && t->Proc.result_count == 0) {
|
||||
// Good
|
||||
} else {
|
||||
gbString str = type_to_string(t);
|
||||
error(e->token, "Testing procedures must have a signature type of proc(), got %s", str);
|
||||
gb_string_free(str);
|
||||
is_tester = false;
|
||||
}
|
||||
|
||||
if (is_tester) {
|
||||
add_dependency_to_set(c, e);
|
||||
array_add(&c->info.testing_procedures, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
add_dependency_to_set(c, start);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_entity_a_dependency(Entity *e) {
|
||||
@@ -4344,6 +4389,7 @@ void check_parsed_files(Checker *c) {
|
||||
string_map_set(&c->info.packages, p->fullpath, p);
|
||||
|
||||
if (scope->flags&ScopeFlag_Init) {
|
||||
c->info.init_package = p;
|
||||
c->info.init_scope = scope;
|
||||
}
|
||||
if (p->kind == Package_Runtime) {
|
||||
@@ -4613,7 +4659,7 @@ void check_parsed_files(Checker *c) {
|
||||
|
||||
|
||||
TIME_SECTION("check entry point");
|
||||
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point) {
|
||||
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
|
||||
Scope *s = c->info.init_scope;
|
||||
GB_ASSERT(s != nullptr);
|
||||
GB_ASSERT(s->flags&ScopeFlag_Init);
|
||||
|
||||
@@ -273,6 +273,7 @@ struct CheckerInfo {
|
||||
|
||||
AstPackage * builtin_package;
|
||||
AstPackage * runtime_package;
|
||||
AstPackage * init_package;
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
@@ -283,6 +284,7 @@ struct CheckerInfo {
|
||||
|
||||
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
|
||||
|
||||
Array<Entity *> testing_procedures;
|
||||
|
||||
bool allow_identifier_uses;
|
||||
Array<Ast *> identifier_uses; // only used by 'odin query'
|
||||
|
||||
+10
-3
@@ -12781,11 +12781,18 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int));
|
||||
|
||||
ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
|
||||
{
|
||||
Array<irValue *> empty_args = {};
|
||||
if (build_context.command_kind == Command_test) {
|
||||
for_array(i, m->info->testing_procedures) {
|
||||
Entity *e = m->info->testing_procedures[i];
|
||||
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||
GB_ASSERT(found != nullptr);
|
||||
ir_emit_call(proc, *found, empty_args);
|
||||
}
|
||||
} else {
|
||||
irValue **found = map_get(&proc->module->values, hash_entity(entry_point));
|
||||
if (found != nullptr) {
|
||||
Array<irValue *> args = {};
|
||||
ir_emit_call(proc, *found, args);
|
||||
ir_emit_call(proc, *found, empty_args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -12875,11 +12875,21 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
lbValue *found = map_get(&m->values, hash_entity(entry_point));
|
||||
GB_ASSERT(found != nullptr);
|
||||
|
||||
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, "");
|
||||
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
|
||||
|
||||
if (build_context.command_kind == Command_test) {
|
||||
for_array(i, m->info->testing_procedures) {
|
||||
Entity *e = m->info->testing_procedures[i];
|
||||
lbValue *found = map_get(&m->values, hash_entity(e));
|
||||
GB_ASSERT(found != nullptr);
|
||||
lb_emit_call(p, *found, {});
|
||||
}
|
||||
} else {
|
||||
lbValue *found = map_get(&m->values, hash_entity(entry_point));
|
||||
GB_ASSERT(found != nullptr);
|
||||
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
|
||||
}
|
||||
|
||||
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
|
||||
+10
-5
@@ -675,7 +675,7 @@ ExactValue build_param_to_exact_value(String name, String param) {
|
||||
bool parse_build_flags(Array<String> args) {
|
||||
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
|
||||
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
|
||||
@@ -693,7 +693,7 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
|
||||
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
|
||||
@@ -1540,6 +1540,8 @@ void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
|
||||
} else if (command == "check") {
|
||||
print_usage_line(1, "check parse and type check .odin file");
|
||||
} else if (command == "test") {
|
||||
print_usage_line(1, "test build ands runs 'test_*' procedures in the initial package");
|
||||
} else if (command == "query") {
|
||||
print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program");
|
||||
} else if (command == "doc") {
|
||||
@@ -1550,9 +1552,9 @@ void print_show_help(String const arg0, String const &command) {
|
||||
|
||||
bool doc = command == "doc";
|
||||
bool build = command == "build";
|
||||
bool run_or_build = command == "run" || command == "build";
|
||||
bool run_or_build = command == "run" || command == "build" || command == "test";
|
||||
bool check_only = command == "check";
|
||||
bool check = command == "run" || command == "build" || command == "check";
|
||||
bool check = run_or_build || command == "check";
|
||||
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(1, "Flags");
|
||||
@@ -1850,12 +1852,15 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
String run_args_string = {};
|
||||
|
||||
bool run_output = false;
|
||||
if (command == "run") {
|
||||
if (command == "run" || command == "test") {
|
||||
if (args.count < 3) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
build_context.command_kind = Command_run;
|
||||
if (command == "test") {
|
||||
build_context.command_kind = Command_test;
|
||||
}
|
||||
|
||||
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
|
||||
defer (array_free(&run_args));
|
||||
|
||||
@@ -107,6 +107,8 @@ struct AstFile {
|
||||
f64 time_to_tokenize; // seconds
|
||||
f64 time_to_parse; // seconds
|
||||
|
||||
bool is_test;
|
||||
|
||||
CommentGroup *lead_comment; // Comment (block) before the decl
|
||||
CommentGroup *line_comment; // Comment after the semicolon
|
||||
CommentGroup *docs; // current docs
|
||||
|
||||
Reference in New Issue
Block a user