From 4140e7e2377fa7ad8a75d4b8b7d82f7cdf545b2b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 4 Oct 2026 18:10:34 -0400 Subject: [PATCH] setting up some tests... --- scripts/Readme.md | 1 + scripts/build.ci.ps1 | 27 ++++++ scripts/clean.ps1 | 4 + test/dogfood_parse/test.cpp | 170 ++++++++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 test/dogfood_parse/test.cpp diff --git a/scripts/Readme.md b/scripts/Readme.md index 2d68977..a7bfabb 100644 --- a/scripts/Readme.md +++ b/scripts/Readme.md @@ -29,6 +29,7 @@ args: parser_bounds lexer_failures parse_body_messages + dogfood_parse clang msvc : By default this project builds with clang, specifying msvc will build with MSVC. debug diff --git a/scripts/build.ci.ps1 b/scripts/build.ci.ps1 index dcacff4..4364429 100644 --- a/scripts/build.ci.ps1 +++ b/scripts/build.ci.ps1 @@ -79,6 +79,7 @@ Push-Location $path_root [bool] $parser_bounds = $false [bool] $lexer_failures = $false [bool] $parse_body_messages = $false +[bool] $dogfood_parse = $false [array] $vendors = @( "clang", "msvc" ) @@ -101,6 +102,7 @@ if ( $args ) { $args | ForEach-Object { "parser_bounds" { $parser_bounds = $true } "lexer_failures" { $lexer_failures = $true } "parse_body_messages" { $parse_body_messages = $true } + "dogfood_parse" { $dogfood_parse = $true } } }} #endregion Arguments @@ -140,6 +142,7 @@ $cannot_build = $cannot_build -and $test -eq $false $cannot_build = $cannot_build -and $parser_bounds -eq $false $cannot_build = $cannot_build -and $lexer_failures -eq $false $cannot_build = $cannot_build -and $parse_body_messages -eq $false +$cannot_build = $cannot_build -and $dogfood_parse -eq $false if ( $cannot_build ) { Stop-stage "No build target specified" } @@ -522,6 +525,30 @@ if ($parse_body_messages) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable invoke-stageTool -stage "parse_body_messages" -compiled $result -executable $executable -path_work $path_probe } + +if ($dogfood_parse) +{ + $path_probe = join-path $path_test dogfood_parse + $path_build = join-path $path_probe build + if ( -not(Test-Path($path_build) )) { + new-item -ItemType Directory -Path $path_build | Out-Null + } + + $includes = @( $path_base ) + $unit = join-path $path_probe "test.cpp" + $executable = join-path $path_build "dogfood_parse.exe" + + $compiler_args = @() + $compiler_args += ( $flag_define + 'GEN_TIME' ) + $compiler_args += $flag_cpp17 + + $linker_args = @( + $flag_link_win_subsystem_console + ) + + $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable + invoke-stageTool -stage "dogfood_parse" -compiled $result -executable $executable -path_work $path_probe +} #endregion Building pop-location # $path_root diff --git a/scripts/clean.ps1 b/scripts/clean.ps1 index 0d32560..b6284ac 100644 --- a/scripts/clean.ps1 +++ b/scripts/clean.ps1 @@ -22,6 +22,7 @@ $path_test_gen = Join-Path $path_test gen $path_parser_bounds_build = Join-Path $path_test "parser_bounds\build" $path_lexer_failures_build = Join-Path $path_test "lexer_failures\build" $path_parse_body_messages_build = Join-Path $path_test "parse_body_messages\build" +$path_dogfood_parse_build = Join-Path $path_test "dogfood_parse\build" $path_x64 = Join-Path $path_root x64 $path_release = Join-Path $path_root release @@ -70,6 +71,9 @@ if ( Test-Path $path_lexer_failures_build ) { if ( Test-Path $path_parse_body_messages_build ) { Remove-Item $path_parse_body_messages_build -Recurse -Verbose } +if ( Test-Path $path_dogfood_parse_build ) { + Remove-Item $path_dogfood_parse_build -Recurse -Verbose +} if ( Test-Path $path_x64) { Remove-Item $path_x64 -Recurse -Verbose } diff --git a/test/dogfood_parse/test.cpp b/test/dogfood_parse/test.cpp new file mode 100644 index 0000000..9274aef --- /dev/null +++ b/test/dogfood_parse/test.cpp @@ -0,0 +1,170 @@ +#define GEN_TIME +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_ENFORCE_STRONG_CODE_TYPES +#include "gen.cpp" + +using namespace gen; + +global int g_failures = 0; + +internal void expect(int condition, Str label) { + if (condition == false) { log_fmt("FAIL %S\n", label); ++g_failures; } +} + +internal Str body_text(CodeBody body) { + return strbuilder_to_str(code_to_strbuilder(cast(Code, body))); +} + +internal b32 body_has_variable(CodeBody body, Str name) { for (Code member : body) { + if (member->Type == CT_Variable && member->Name.is_equal(name)) return true; + if ((member->Type == CT_Struct || member->Type == CT_Union) && member->Body.ast != nullptr) { + if (body_has_variable(cast(CodeBody, member->Body), name)) + return true; + } +} return false; } + +internal Str bad_at(Str good) { + StrBuilder built = strbuilder_make_reserve(_ctx->Allocator_Temp, good.Len + 2); + strbuilder_append_str(& built, good); + strbuilder_append_str(& built, txt("\n@")); + return strbuilder_to_str(built); +} + +internal void expect_context_good(Context* ctx, Str label) { + Str def = txt("struct Context\n{\n\tContext* parent;\n};\n\nContext* make_context();\n"); + CodeBody body = parse_global_body(def); + Code found = InvalidCode; + for (Code code : body) if (code->Type == CT_Struct && code->Name.is_equal(txt("Context"))) + found = code; + expect(found.ast != nullptr, label); if (found.ast != nullptr) { + Code member = InvalidCode; + for (Code code : cast(CodeBody, found->Body)) { + if (code->Type == CT_Variable && code->Name.is_equal(txt("parent"))) + member = code; + } + expect(member.ast != nullptr, label); + } + expect(ctx->parse_info.messages == nullptr, label); + expect(ctx->parse_info.lexed.messages == nullptr, label); +} + +internal void expect_context_bad(Str label) { + Context ctx = {}; init(& ctx); + Str good = txt("struct Context\n{\n\tContext* parent;\n};\n\nContext* make_context();\n"); + CodeBody body = parse_global_body(bad_at(good)); + Str text = body_text(body); + expect(str_contains(text, txt("Context")), label); + expect(str_contains(text, txt("parent")), label); + expect(ctx.parse_info.lexed.messages != nullptr, label); + if (ctx.parse_info.lexed.messages != nullptr) expect(str_contains(ctx.parse_info.lexed.messages->content, txt("Failed to lex token")), label); + expect_context_good(&ctx, txt("context-reuse")); + deinit(& ctx); +} + +internal Str vec_def() { + Str type = txt("Vec2f"); + Str unit_type = txt("f32"); + return token_fmt("type", type, "unit_type", unit_type, stringize( + struct + { + union { + struct { + x; + y; + }; + Basis[2]; + }; + }; + )); +} + +internal void expect_vec_good(Context* ctx, Str label) { + Str def = vec_def(); + expect(str_contains(def, txt("struct Vec2f")), txt("vec-def-struct")); + expect(str_contains(def, txt("union")), txt("vec-def-union")); + expect(str_contains(def, txt("f32 x;")), txt("vec-def-x")); + expect(str_contains(def, txt("f32 y;")), txt("vec-def-y")); + expect(str_contains(def, txt("f32 Basis[2]")), txt("vec-def-Basis")); + CodeBody body = parse_global_body(def); + expect(body_has_variable(body, txt("x")), txt("vec-ast-x")); + expect(body_has_variable(body, txt("y")), txt("vec-ast-y")); + Str text = body_text(body); + expect(str_contains(text, txt("Vec2f")), txt("vec-name")); + expect(str_contains(text, txt("x")), txt("vec-x")); + expect(str_contains(text, txt("y")), txt("vec-y")); + expect(ctx->parse_info.messages == nullptr, txt("vec-parser-messages")); + expect(ctx->parse_info.lexed.messages == nullptr, txt("vec-lexer-messages")); +} + +internal void expect_vec_bad(Str label) { + Context ctx = {}; init(& ctx); + CodeBody body = parse_global_body(bad_at(vec_def())); + Str text = body_text(body); + expect(str_contains(text, txt("Vec2f")), label); + expect(ctx.parse_info.lexed.messages != nullptr, label); + if (ctx.parse_info.lexed.messages != nullptr) expect(str_contains(ctx.parse_info.lexed.messages->content, txt("Failed to lex token")), label); + expect_vec_good(& ctx, txt("vec-reuse")); + deinit(& ctx); +} + +internal Str unreal_def() { return txt("UCLASS()\nclass UGasaFixture\n{\n\tUPROPERTY()\n\tint Health;\n};\n"); } + +internal void expect_unreal_good(Context* ctx, Str label) { + CodeBody body = parse_global_body(unreal_def()); + Code found = InvalidCode; + for (Code code : body) if (code->Type == CT_Class && code->Name.is_equal(txt("UGasaFixture"))) + found = code; + expect(found.ast != nullptr, label); if (found.ast != nullptr) { + b32 saw_prop = false; + b32 saw_health = false; + for (Code member : cast(CodeBody, found->Body)) { + if ( member->Type == CT_Untyped && member->Name.starts_with(txt("UPROPERTY"))) saw_prop = true; + if (saw_prop && member->Type == CT_Variable && member->Name.is_equal(txt("Health"))) saw_health = true; + } + expect(saw_prop, label); + expect(saw_health, label); + } + expect(ctx->parse_info.messages == nullptr, label); + expect(ctx->parse_info.lexed.messages == nullptr, label); +} + +internal void expect_unreal_bad(Str label) { + Context ctx = {}; init(& ctx); + register_macros(args( + (Macro { txt("UCLASS"), MT_Statement, MF_Functional }), + (Macro { txt("UPROPERTY"), MT_Statement, MF_Functional }) + )); + CodeBody body = parse_global_body(bad_at(unreal_def())); + Str text = body_text(body); + expect(str_contains(text, txt("UGasaFixture")), label); + expect(ctx.parse_info.lexed.messages != nullptr, label); + if (ctx.parse_info.lexed.messages != nullptr) expect(str_contains(ctx.parse_info.lexed.messages->content, txt("Failed to lex token")), label); + expect_unreal_good(&ctx, txt("unreal-reuse")); + deinit(& ctx); +} + +int main() +{ + Context ctx = {}; init(& ctx); + expect_context_good(&ctx, txt("context")); + deinit(& ctx); + expect_context_bad(txt("context-at")); + + init(& ctx); + expect_vec_good(& ctx, txt("vec")); + deinit(& ctx); + expect_vec_bad(txt("vec-at")); + + init(& ctx); + register_macros(args( + (Macro { txt("UCLASS"), MT_Statement, MF_Functional }), + (Macro { txt("UPROPERTY"), MT_Statement, MF_Functional }) + )); + expect_unreal_good(& ctx, txt("unreal")); + deinit(& ctx); + expect_unreal_bad(txt("unreal-at")); + + if (g_failures != 0) { log_fmt("dogfood parse fixtures failed: %d\n", g_failures); return 1; } + log_fmt("dogfood parse fixtures passed\n"); + return 0; +}