From 61d53362e856b7ca034892a79e0792cb15a6e0d7 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 3 Oct 2026 14:46:07 -0400 Subject: [PATCH] reviewing project state, doing some tests. --- .gitignore | 3 - base/helpers/misc.hpp | 12 +- scripts/build.ci.ps1 | 200 ++++++++++--------- scripts/build.ps1 | 2 +- scripts/helpers/vendor_toolchain.ps1 | 26 ++- scripts/verify_baseline.ps1 | 267 ++++++++++++++++++++++++++ test/Readme.md | 16 +- test/c_library/test.c | 69 ++++--- test/cpp_library/test.cpp | 60 ++++-- test/verification/failure_assert.cpp | 10 + test/verification/failure_compile.cpp | 3 + test/verification/failure_format.cmd | 2 + test/verification/failure_link.cpp | 7 + test/verification/failure_refactor.c | 4 + test/verification/stale_executable.c | 4 + 15 files changed, 530 insertions(+), 155 deletions(-) create mode 100644 scripts/verify_baseline.ps1 create mode 100644 test/verification/failure_assert.cpp create mode 100644 test/verification/failure_compile.cpp create mode 100644 test/verification/failure_format.cmd create mode 100644 test/verification/failure_link.cpp create mode 100644 test/verification/failure_refactor.c create mode 100644 test/verification/stale_executable.c diff --git a/.gitignore b/.gitignore index b011ad8..893d2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,3 @@ test/c_library/gen test/cpp_library/gen !scripts/helpers/refactor.exe - -# ai/** -.ai diff --git a/base/helpers/misc.hpp b/base/helpers/misc.hpp index 2db55f1..2de6a1f 100644 --- a/base/helpers/misc.hpp +++ b/base/helpers/misc.hpp @@ -37,7 +37,11 @@ void clang_format_file( char const* path, char const* style_path ) strbuilder_append_str( & command, cf_verbose ); strbuilder_append_string( & command, style_arg ); strbuilder_append_string( & command, resolved_path ); - system( command ); + int command_result = system( command ); + if ( command_result != 0 ) { + log_fmt( "clang-format failed with status %d\n", command_result ); + process_exit( 1 ); + } } // Will refactor a file with the given script at the provided path. @@ -57,7 +61,11 @@ void refactor_file( char const* path, char const* refactor_script ) strbuilder_append_str( & command, txt("-num=1 ") ); strbuilder_append_fmt( & command, "-src=%s ", path ); strbuilder_append_fmt( & command,"-spec=%s ", refactor_script ); - system(command); + int command_result = system( command ); + if ( command_result != 0 ) { + log_fmt( "refactor failed with status %d\n", command_result ); + process_exit( 1 ); + } log_fmt("\n"); } diff --git a/scripts/build.ci.ps1 b/scripts/build.ci.ps1 index 3809bf3..3198f9e 100644 --- a/scripts/build.ci.ps1 +++ b/scripts/build.ci.ps1 @@ -10,7 +10,57 @@ $vendor_toolchain = Join-Path $PSScriptRoot 'helpers/vendor_toolchain.ps1' Import-Module $misc -$path_root = Get-ScriptRepoRoot +function stop-stage { + param([string]$stage, [int]$code_exit = 1) + Write-Host "FAILED: $stage" -ForegroundColor Red + exit $code_exit +} + +function invoke-stageTool { + param( + [string]$stage, + [bool]$compiled, + [string]$executable, + [string]$path_work + ) + if (-not $compiled) { + stop-stage "$stage compile/link" + } + if (-not (Test-Path -LiteralPath $executable)) { + stop-stage "$stage missing output $executable" + } + $code_exit = 1 + push-location $path_work + try { + & $executable + $code_exit = $LASTEXITCODE + } + finally { + pop-location + } + if ($code_exit -ne 0) { + stop-stage "$stage run" $code_exit + } + Write-Host "PASSED: $stage" +} + +if ($env:GENCPP_SOURCE_ROOT) { + $path_root = (Resolve-Path -LiteralPath $env:GENCPP_SOURCE_ROOT).Path + $probe = Join-Path $path_root "base\base.cpp" + if (-not (Test-Path -LiteralPath $probe)) { + stop-stage "GENCPP_SOURCE_ROOT is not a gencpp tree: $path_root" + } +} +else { + $path_root = Get-ScriptRepoRoot +} +Write-Host "SOURCE_ROOT: $path_root" +if ($null -eq $is_windows) { + $is_windows = [System.Environment]::OSVersion.Platform -eq 'Win32NT' +} +if ($null -eq $is_linux) { + $is_linux = [System.Environment]::OSVersion.Platform -eq 'Unix' +} Push-Location $path_root @@ -50,7 +100,7 @@ if ( $args ) { $args | ForEach-Object { #endregion Arguments #region Configuration -if ($IsWindows) { +if ($is_windows) { # This library was really designed to only run on 64-bit systems. # (Its a development tool after all) & $devshell -arch amd64 @@ -81,7 +131,7 @@ $cannot_build = $cannot_build -and $c_lib_dyn -eq $false $cannot_build = $cannot_build -and $unreal -eq $false $cannot_build = $cannot_build -and $test -eq $false if ( $cannot_build ) { - throw "No build target specified. One must be specified, this script will not assume one" + Stop-stage "No build target specified" } . $vendor_toolchain @@ -125,18 +175,7 @@ if ( $base ) $executable = join-path $path_build "base.exe" $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_base - if ( Test-Path( $executable ) ) { - write-host "`nRunning base" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nbase completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "base" -compiled $result -executable $executable -path_work $path_base } if ( $segmented ) @@ -163,18 +202,7 @@ if ( $segmented ) $executable = join-path $path_build "segmented.exe" $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_segmented - if ( Test-Path( $executable ) ) { - write-host "`nRunning segmented" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nSegmented completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "segmented" -compiled $result -executable $executable -path_work $path_segmented } if ( $singleheader ) @@ -201,18 +229,7 @@ if ( $singleheader ) ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_singleheader - if ( Test-Path( $executable ) ) { - write-host "`nRunning singleheader generator" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nSingleheader generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "singleheader" -compiled $result -executable $executable -path_work $path_singleheader } if ( $c_lib -or $c_lib_static -or $c_lib_dyn ) @@ -239,18 +256,7 @@ if ( $c_lib -or $c_lib_static -or $c_lib_dyn ) ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_c_library - if ( Test-Path( $executable ) ) { - write-host "`nRunning c_library generator" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nc_library generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "c_lib" -compiled $result -executable $executable -path_work $path_c_library } if ( $c_lib_static ) @@ -267,6 +273,7 @@ if ( $c_lib_static ) $linker_args = @() $result = build-simple $path_build $includes $compiler_args $linker_args $unit $path_lib + if (-not $result) { Stop-stage "c_lib_static compile/link" } } if ( $c_lib_dyn ) @@ -285,6 +292,7 @@ if ( $c_lib_dyn ) $linker_args = @() $result = build-simple $path_build $includes $compiler_args $linker_args $unit $path_dll + if (-not $result) { Stop-stage "c_lib_dyn compile/link" } } if ( $unreal ) @@ -311,23 +319,27 @@ if ( $unreal ) ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_unreal - if ( Test-Path( $executable ) ) { - write-host "`nRunning unreal variant generator" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`n Unreal variant generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "unreal" -compiled $result -executable $executable -path_work $path_unreal . $refactor_unreal } # C Library testing +if ($test) { + $consumer_headers = @( + (Join-Path $path_c_library 'gen\gen_singleheader.h'), + (Join-Path $path_singleheader 'gen\gen.hpp') + ) + $header_hashes_before = @{} + foreach ($header in $consumer_headers) { + if (-not (Test-Path -LiteralPath $header -PathType Leaf)) { + Stop-stage "test missing generated header $header" + } + $header_hashes_before[$header] = (Get-FileHash -LiteralPath $header -Algorithm SHA256).Hash + Write-Host "HEADER_SHA256_GENERATED: $($header_hashes_before[$header]) $header" + } +} + if ( $test -and $true ) { $path_test_c = join-path $path_test c_library @@ -350,24 +362,14 @@ if ( $test -and $true ) $compiler_args += $flag_all_c $compiler_args += $flag_updated_cpp_macro $compiler_args += $flag_c11 + $compiler_args += "-H" $linker_args = @( $flag_link_win_subsystem_console ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_test_c - if ( Test-Path( $executable ) ) { - write-host "`nRunning c_library test" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nc_library generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "test-c11" -compiled $result -executable $executable -path_work $path_test_c } if ( $test -and $false ) @@ -398,30 +400,18 @@ if ( $test -and $false ) ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_test_c - if ( Test-Path( $executable ) ) { - write-host "`nRunning c_library test" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nc_library generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location } -if ($test -and $false) +if ($test) { $path_test_cpp = join-path $path_test cpp_library $path_build = join-path $path_test_cpp build $path_gen = join-path $path_test_cpp gen if ( -not(Test-Path($path_build) )) { - New-Item -ItemType Directory -Path $path_build + new-item -ItemType Directory -Path $path_build } if ( -not(Test-Path($path_gen) )) { - New-Item -ItemType Directory -Path $path_gen + new-item -ItemType Directory -Path $path_gen } $path_singleheader_include = join-path $path_singleheader gen @@ -431,25 +421,29 @@ if ($test -and $false) $compiler_args = @() $compiler_args += ( $flag_define + 'GEN_TIME' ) + $compiler_args += @("-x", "c++") + $compiler_args += "-std=c++17" + $compiler_args += ($flag_define + "GEN_BUILD_DEBUG=1") + $compiler_args += "-H" $linker_args = @( $flag_link_win_subsystem_console ) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable - - Push-Location $path_test_cpp - if ( Test-Path( $executable ) ) { - write-host "`nRunning cpp_library test" - $time_taken = Measure-Command { & $executable - | ForEach-Object { - write-host `t $_ -ForegroundColor Green - } - } - write-host "`nc_library generator completed in $($time_taken.TotalMilliseconds) ms" - } - Pop-Location + invoke-stageTool -stage "test-cpp" -compiled $result -executable $executable -path_work $path_test_cpp +} +if ($test) { + foreach ($header in $consumer_headers) { + $hash = (Get-FileHash -LiteralPath $header -Algorithm SHA256).Hash + write-host "HEADER_SHA256_CONSUMED: $hash $header" + if ($hash -ne $header_hashes_before[$header]) { + Stop-stage "test header changed after generation $header" + } + } } #endregion Building -Pop-Location # $path_root +pop-location # $path_root +write-host "PASSED: build.ci.ps1" +exit 0 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 349c629..9f052d8 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,3 +1,3 @@ -cls $build = Join-Path $PSScriptRoot 'build.ci.ps1' & $build @args +exit $LASTEXITCODE diff --git a/scripts/helpers/vendor_toolchain.ps1 b/scripts/helpers/vendor_toolchain.ps1 index c3c7e96..7df5259 100644 --- a/scripts/helpers/vendor_toolchain.ps1 +++ b/scripts/helpers/vendor_toolchain.ps1 @@ -3,7 +3,7 @@ import-module $target_arch -if ($IsWindows) { +if ($is_windows) { # This HandmadeHero implementation is only designed for 64-bit systems & $devshell -arch amd64 } @@ -27,6 +27,12 @@ function run-archiver { param( $archiver, $library, $lib_args ) + if (-not (Get-Command $archiver -ErrorAction SilentlyContinue)) { + Write-Host "FAILED: archiver launch $archiver" -ForegroundColor Red + return $false + } + Write-Host ("ARCHIVER_ARGV: " + ($lib_args -join " ")) + write-host "`Creating library $library" if ( $verbose ) { write-host "Lib manager config:" @@ -60,9 +66,15 @@ function run-compiler { param( $compiler, $unit, $compiler_args ) + if (-not (Get-Command $compiler -ErrorAction SilentlyContinue)) { + Write-Host "FAILED: compiler launch $compiler" -ForegroundColor Red + return $false + } + if ( $analysis ) { $compiler_args += $flag_syntax_only } + Write-Host ("COMPILER_ARGV: " + ($compiler_args -join " ")) write-host "`Compiling $unit" if ( $verbose ) { @@ -97,6 +109,12 @@ function run-linker { param( $linker, $binary, $linker_args ) + if (-not (Get-Command $linker -ErrorAction SilentlyContinue)) { + Write-Host "FAILED: linker launch $linker" -ForegroundColor Red + return $false + } + Write-Host ("LINKER_ARGV: " + ($linker_args -join " ")) + write-host "`Linking $binary" if ( $verbose ) { write-host "Linker config:" @@ -147,12 +165,12 @@ if ( $vendor -match "clang" ) $flag_library = '-l' $flag_library_path = '-L' $flag_linker = '-Wl,' - if ( $IsWindows ) { + if ( $is_windows ) { $flag_link_dll = '/DLL' $flag_link_mapfile = '/MAP:' $flag_link_optimize_references = '/OPT:REF' } - if ( $IsLinux ) { + if ( $is_linux ) { $flag_link_mapfile = '--Map=' $flag_link_optimize_references = '--gc-sections' } @@ -191,7 +209,7 @@ if ( $vendor -match "clang" ) ) # https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170 - if ( $IsWindows ) { + if ( $is_windows ) { $libraries = @( 'Kernel32' # For Windows API # 'msvcrt', # For the C Runtime (Dynamically Linked) diff --git a/scripts/verify_baseline.ps1 b/scripts/verify_baseline.ps1 new file mode 100644 index 0000000..057ce67 --- /dev/null +++ b/scripts/verify_baseline.ps1 @@ -0,0 +1,267 @@ +param( + [Parameter(Mandatory = $true)][Alias('MirrorRoot')][string]$path_mirror, + [ValidateSet( + "measure", + "positive", + "negative-compile", + "negative-link", + "negative-launch", + "negative-stale", + "negative-stale-header", + "negative-format", + "negative-refactor", + "negative-assert" + )] + [string]$mode = "positive", + [Alias('SourceRoot')][string]$path_source_root = "C:\projects\gencpp", + [Alias('ReuseMirror')][switch]$reuse_mirror +) + +$ErrorActionPreference = "Stop" +$path_source = (Resolve-Path -LiteralPath $path_source_root).Path +$path_mirror = [System.IO.Path]::GetFullPath($path_mirror) +$path_verification_root = Join-Path $path_mirror 'test\verification' +if ($path_mirror -eq $path_source) { throw "MirrorRoot must not be the project checkout" } +$prefix_source = $path_source.TrimEnd('\') + '\' +$prefix_mirror = $path_mirror.TrimEnd('\') + '\' +if ($path_source.StartsWith($prefix_mirror, [System.StringComparison]::OrdinalIgnoreCase) -or + $path_mirror.StartsWith($prefix_source, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "path_mirror and path_source_root must not overlap" +} +if ($reuse_mirror -and $mode -ne "positive") { throw "reuse_mirror is for the positive mode only" } +if ($reuse_mirror -and -not (Test-Path -LiteralPath (Join-Path $path_mirror 'base\base.cpp'))) { + throw "reuse_mirror needs an existing baseline mirror" +} + +function Copy-Snapshot([string]$path_destination) +{ + if (Test-Path -LiteralPath $path_destination) { Remove-Item -LiteralPath $path_destination -Recurse -Force } + New-Item -ItemType Directory -Path $path_destination | Out-Null + & robocopy $path_source $path_destination /E /NFL /NDL /NJH /NJS /XD .git + if ($LASTEXITCODE -ge 8) { throw "robocopy failed: $LASTEXITCODE" } + $global:LASTEXITCODE = 0 + @( + "gen_segmented\gen", + "gen_segmented\build", + "gen_singleheader\gen", + "gen_singleheader\build", + "gen_c_library\gen", + "gen_c_library\build", + "gen_unreal_engine\gen", + "gen_unreal_engine\build", + "test\c_library\build", + "test\c_library\gen", + "test\cpp_library\build", + "test\cpp_library\gen", + "base\build" + ) | ForEach-Object { + $path_item = Join-Path $path_destination $_ + if (Test-Path -LiteralPath $path_item) { Remove-Item -LiteralPath $path_item -Recurse -Force } + } +} + +function Copy-VerificationFixture([string]$name_fixture, [string]$path_destination) { + $path_fixture = Join-Path $path_verification_root $name_fixture + if (-not (Test-Path -LiteralPath $path_fixture -PathType Leaf)) { + throw "Missing verification fixture: $path_fixture" + } + Copy-Item -LiteralPath $path_fixture -Destination $path_destination -Force +} + +function Invoke-Driver([string]$path_tree, [string[]]$list_targets) { + $path_driver = Join-Path $path_tree "scripts\build.ci.ps1" + $name_log = if ($reuse_mirror) { 'verify_driver.repeat.log' } else { 'verify_driver.log' } + $path_log = Join-Path $path_tree $name_log + $path_previous_root = $env:GENCPP_SOURCE_ROOT + $env:GENCPP_SOURCE_ROOT = $path_tree + $line_args = $list_targets -join ' ' + try { + $text_output = & powershell.exe -NoProfile -Command "& '$path_driver' clang debug $line_args; exit `$LASTEXITCODE" *>&1 + $code_exit = $LASTEXITCODE + } + finally { + $env:GENCPP_SOURCE_ROOT = $path_previous_root + } + $text_output | Tee-Object -FilePath $path_log | Out-Host + $script:path_last_driver_log = $path_log + $script:code_last_driver = $code_exit + return $code_exit +} + +function Get-DriverLogText { + if (-not $script:path_last_driver_log) { return "" } + if (-not (Test-Path -LiteralPath $script:path_last_driver_log)) { return "" } + return Get-Content -LiteralPath $script:path_last_driver_log -Raw +} + +function Assert-PositiveOutputs([string]$path_tree) { + $list_outputs = @( + 'base\components\gen\ecodetypes.hpp', + 'base\components\gen\eoperator.hpp', + 'base\components\gen\especifier.hpp', + 'base\components\gen\etoktype.hpp', + 'base\components\gen\ast_inlines.hpp', + 'gen_segmented\gen\gen.hpp', + 'gen_segmented\gen\gen.cpp', + 'gen_segmented\gen\gen.dep.hpp', + 'gen_segmented\gen\gen.dep.cpp', + 'gen_segmented\gen\gen.builder.hpp', + 'gen_segmented\gen\gen.builder.cpp', + 'gen_segmented\gen\gen.scanner.hpp', + 'gen_segmented\gen\gen.scanner.cpp', + 'gen_singleheader\gen\gen.hpp', + 'gen_c_library\gen\gen.h', + 'gen_c_library\gen\gen.c', + 'gen_c_library\gen\gen.dep.h', + 'gen_c_library\gen\gen.dep.c', + 'gen_c_library\gen\gen_singleheader.h' + ) + $list_hashes = foreach ($path_relative in $list_outputs) { + $path_output = Join-Path $path_tree $path_relative + if (-not (Test-Path -LiteralPath $path_output -PathType Leaf)) { throw "Missing output: $path_output" } + if ((Get-Item -LiteralPath $path_output).Length -eq 0) { throw "Empty output: $path_output" } + "{0} {1}" -f (Get-FileHash -LiteralPath $path_output -Algorithm SHA256).Hash, $path_relative + } + $text_log = (Get-DriverLogText).Replace('\\', '\') + foreach ($path_relative in @('gen_singleheader\gen\gen.hpp', 'gen_c_library\gen\gen_singleheader.h')) { + $path_header = Join-Path $path_tree $path_relative + if ($text_log -notmatch [regex]::Escape($path_header)) { throw "Compiler trace missing $path_header" } + } + $name_hash_file = if ($reuse_mirror) { 'verify_outputs.repeat.sha256' } else { 'verify_outputs.sha256' } + $list_hashes | Set-Content -LiteralPath (Join-Path $path_tree $name_hash_file) + Write-Host "PASSED: outputs and consumer header traces ($name_hash_file)" +} + +$list_targets = @("base", "segmented", "singleheader", "c_lib", "test") +if (-not $reuse_mirror) { Copy-Snapshot $path_mirror } + +if ($mode -eq "measure" -or $mode -eq "positive") { + $code_exit = Invoke-Driver $path_mirror $list_targets + if ($mode -eq "positive" -and $code_exit -ne 0) { exit $code_exit } + if ($mode -eq "positive") { Assert-PositiveOutputs $path_mirror } + exit $code_exit +} + +if ($mode -eq "negative-compile") { + Copy-VerificationFixture 'failure_compile.cpp' (Join-Path $path_mirror 'base\base.cpp') + $code_exit = Invoke-Driver $path_mirror @("base") + if ($code_exit -eq 0) { throw "negative-compile returned 0" } + exit 0 +} + +if ($mode -eq "negative-link") { + Copy-VerificationFixture 'failure_link.cpp' (Join-Path $path_mirror 'base\base.cpp') + $code_exit = Invoke-Driver $path_mirror @("base") + $text_log = Get-DriverLogText + if ($code_exit -eq 0 -or $text_log -notmatch "Linking failed") { + throw "negative-link did not fail at the linker" + } + exit 0 +} + +if ($mode -eq "negative-launch") { + $path_toolchain = Join-Path $path_mirror "scripts\helpers\vendor_toolchain.ps1" + $text_toolchain = Get-Content -LiteralPath $path_toolchain -Raw + $text_patched = $text_toolchain.Replace( + "`$linker = 'lld-link'", + "`$linker = 'gencpp-missing-linker'" + ) + if ($text_patched -eq $text_toolchain) { throw "negative-launch could not replace the linker path" } + + Set-Content -LiteralPath $path_toolchain -Value $text_patched -NoNewline + $code_exit = Invoke-Driver $path_mirror @("base") + $text_log = Get-DriverLogText + if ($code_exit -eq 0 -or $text_log -notmatch "FAILED: linker launch gencpp-missing-linker") { + throw "negative-launch did not report a missing linker" + } + exit 0 +} + +if ($mode -eq "negative-stale") { + $path_stale_source = Join-Path $path_mirror "stale_marker.c" + $path_stale_executable = Join-Path $path_mirror "stale_marker.exe" + Copy-VerificationFixture 'stale_executable.c' $path_stale_source + + & clang -o $path_stale_executable $path_stale_source + if ($LASTEXITCODE -ne 0) { throw "failed to compile stale marker" } + + $path_base_build = Join-Path $path_mirror "base\build" + New-Item -ItemType Directory -Path $path_base_build -Force | Out-Null + Copy-Item -LiteralPath $path_stale_executable -Destination (Join-Path $path_base_build "base.exe") -Force + Copy-VerificationFixture 'failure_compile.cpp' (Join-Path $path_mirror 'base\base.cpp') + + $code_exit = Invoke-Driver $path_mirror @("base") + if ($code_exit -eq 0) { throw "negative-stale returned 0" } + if ($code_exit -eq 73) { throw "negative-stale ran the stale executable" } + exit 0 +} + +if ($mode -eq "negative-stale-header") { + $code_exit = Invoke-Driver $path_mirror @("base", "segmented", "singleheader", "c_lib") + if ($code_exit -ne 0) { throw "negative-stale-header setup generation failed: $code_exit" } + + Copy-Item -LiteralPath $script:path_last_driver_log -Destination (Join-Path $path_mirror 'verify_driver.setup.log') + $path_header = Join-Path $path_mirror 'gen_c_library\gen\gen_singleheader.h' + if (-not (Test-Path -LiteralPath $path_header)) { throw 'negative-stale-header missing setup header' } + + Copy-VerificationFixture 'failure_compile.cpp' (Join-Path $path_mirror 'gen_c_library\c_library.cpp') + $code_exit = Invoke-Driver $path_mirror @('c_lib', 'test') + $text_log = Get-DriverLogText + if ($code_exit -eq 0 -or $text_log -notmatch 'FAILED: c_lib compile/link') { + throw 'negative-stale-header did not fail at c_lib' + } + if ($text_log -match 'Compiling .*test[.]c|PASSED: test-c11|PASSED: test-cpp') { + throw 'negative-stale-header consumed a prior header' + } + exit 0 +} + +if ($mode -eq "negative-format") { + $path_fake_tools = Join-Path $path_mirror "fake-tools" + New-Item -ItemType Directory -Path $path_fake_tools -Force | Out-Null + Copy-VerificationFixture 'failure_format.cmd' (Join-Path $path_fake_tools 'clang-format.cmd') + $path_saved = $env:PATH + $env:PATH = "$path_fake_tools;$env:PATH" + try { + $code_exit = Invoke-Driver $path_mirror @("base") + } + finally { + $env:PATH = $path_saved + } + $text_log = Get-DriverLogText + if ($code_exit -eq 0) { throw "negative-format returned 0" } + if ($text_log -notmatch "clang-format failed") { throw "negative-format log missing clang-format failed" } + exit 0 +} + +if ($mode -eq "negative-refactor") { + $path_fake_source = Join-Path $path_mirror 'failing_refactor.c' + $path_fake_executable = Join-Path $path_mirror 'scripts\helpers\refactor.exe' + Copy-VerificationFixture 'failure_refactor.c' $path_fake_source + & clang -o $path_fake_executable $path_fake_source + if ($LASTEXITCODE -ne 0) { throw 'negative-refactor failed to compile the stub' } + + $code_exit = Invoke-Driver $path_mirror @('c_lib') + $text_log = Get-DriverLogText + if ($code_exit -eq 0 -or $text_log -notmatch 'refactor failed') { + throw 'negative-refactor did not propagate subprocess failure' + } + exit 0 +} + +if ($mode -eq "negative-assert") { + $path_header = Join-Path $path_mirror "gen_singleheader\gen\gen.hpp" + if (-not (Test-Path -LiteralPath $path_header)) { + $code_exit = Invoke-Driver $path_mirror $list_targets + if ($code_exit -ne 0) { throw "negative-assert setup generation failed: $code_exit" } + } + + Copy-VerificationFixture 'failure_assert.cpp' (Join-Path $path_mirror 'test\cpp_library\test.cpp') + $code_exit = Invoke-Driver $path_mirror @("test") + $text_log = Get-DriverLogText + if ($code_exit -eq 0) { throw "negative-assert returned 0" } + if ($text_log -notmatch "FAIL forced assertion") { throw "negative-assert log missing forced assertion marker" } + exit 0 +} + +throw "unhandled mode $mode" diff --git a/test/Readme.md b/test/Readme.md index 616b547..d2002cb 100644 --- a/test/Readme.md +++ b/test/Readme.md @@ -1,3 +1,15 @@ -# Test +# Baseline verification -The implementation here has been gutted and will be rewritten... +The implementation smoke is `verify_baseline.ps1`. It is not a parser corpus. + +From the project root, in a fresh PowerShell process: + +```powershell +.\scripts\verify_baseline.ps1 -Mode positive -MirrorRoot ..\gencpp-verify-positive +.\scripts\verify_baseline.ps1 -Mode positive -MirrorRoot ..\gencpp-verify-positive -ReuseMirror +``` + +The first command generates base component tables, segmented C++, single-header C++, and both C11 layouts. +It compiles and runs the C++17 and C11 single-header host smokes. The second command reuses the same mirror. + +The verification wrapper copies them into the disposable mirror; it does not embed C or C++ source text. diff --git a/test/c_library/test.c b/test/c_library/test.c index 2dbf524..84842f7 100644 --- a/test/c_library/test.c +++ b/test/c_library/test.c @@ -1,36 +1,57 @@ -#if INTELLISENSE_DIRECTIVES -#include "../../gen_c_library/gen/gen_singleheader.h" -#endif - #define GEN_IMPLEMENTATION #define GEN_DEFINE_LIBRARY_CODE_CONSTANTS #include "gen_singleheader.h" -#define gen_iterator( Type, container, iter ) \ - gen_begin_ ## Type(container); \ - iter != gen_end_ ## Type(container); \ - code = gen_next_ ## Type(container, iter) +gen_global gen_s32 g_failures = 0; + +gen_internal void expect(int condition, gen_Str name) { + if (condition == false) { gen_log_fmt("FAIL %S\n", name); ++ g_failures; } +} + +gen_internal void expect_fragments(gen_Str text, gen_Str label) { + expect(text.Ptr != 0 && text.Len > 0, label); + expect(gen_str_contains(text, gen_txt("baseline_message")), label); + expect(gen_str_contains(text, gen_txt("char")), label); + expect(gen_str_contains(text, gen_txt("const")), label); + expect(gen_str_contains(text, gen_txt("*")), label); + expect(gen_str_contains(text, gen_txt("GENCPP_BASELINE")), label); +} + +gen_internal void expect_variable(gen_CodeVar var, gen_Str label) { + expect(var != 0, label); if (var == 0) return; + expect(var->Type == CT_Variable, label); + expect(gen_str_contains(var->Name, gen_txt("baseline_message")), label); + expect(var->ValueType != 0, label); + expect(var->Value != 0, label); + if (var->Value != 0) { + expect(gen_str_contains(var->Value->Content, gen_txt("GENCPP_BASELINE")), label); + } +} int main() { - gen_Context ctx = {0}; - gen_init(& ctx); + gen_Context ctx = {0}; gen_init(&ctx); - gen_Builder src_hello = gen_builder_open("gen/hello.c"); - gen_CodeVar hello_var = gen_parse_variable(code( - char const* hello_gencpp_str = "HELLO GENCPP C11 !"; - )); - gen_builder_print( & src_hello, hello_var ); - gen_builder_write(& src_hello); + gen_CodeTypename type = gen_def_type(name(char), .specifiers = gen_def_specifiers(2, Spec_Const, Spec_Ptr) ); + gen_CodeVar upfront = gen_def_variable(type, name(baseline_message), + .value = gen_untyped_str(code("\"GENCPP_BASELINE\"")) + ); + expect_variable(upfront, gen_txt("c11 upfront")); + expect_fragments(gen_strbuilder_to_str(gen_code_to_strbuilder(upfront)), gen_txt("c11 upfront serialized")); - gen_CodeBody body = gen_parse_file("gen/hello.c"); - for (gen_Code code = gen_iterator(CodeBody, body, code)) switch (code->Type) { - case CT_Variable: - gen_CodeVar var = (gen_CodeVar) code; - gen_log_fmt("%S", var->Value); - break; - } + gen_Code untyped = gen_code_str(char const* baseline_message = "GENCPP_BASELINE"; ); + expect(untyped != 0, gen_txt("c11 untyped")); - gen_deinit(& ctx); + gen_Str untyped_text = gen_strbuilder_to_str(gen_code_to_strbuilder(untyped)); + expect_fragments(untyped_text, gen_txt("c11 untyped content")); + expect_variable(gen_parse_variable(untyped_text), gen_txt("c11 untyped reparsed")); + + gen_CodeVar parsed = gen_parse_variable(code(char const* baseline_message = "GENCPP_BASELINE"; )); + expect_variable(parsed, gen_txt("c11 parsed")); + expect_fragments(gen_strbuilder_to_str(gen_code_to_strbuilder(parsed)), gen_txt("c11 parsed serialized")); + + gen_deinit(&ctx); + if (g_failures != 0) { gen_log_fmt("c11 baseline smoke failed: %d\n", g_failures); return 1; } + gen_log_fmt("c11 baseline smoke passed\n"); return 0; } diff --git a/test/cpp_library/test.cpp b/test/cpp_library/test.cpp index 8ee2088..1861446 100644 --- a/test/cpp_library/test.cpp +++ b/test/cpp_library/test.cpp @@ -3,27 +3,55 @@ #define GEN_ENFORCE_STRONG_CODE_TYPES #include "gen.hpp" +global int g_failures = 0; + +internal void expect(int condition, gen::Str name) { + if (condition == false) { gen::log_fmt("FAIL %S\n", name); ++ g_failures; } +} + +internal void expect_fragments(gen::Str text, gen::Str label) { + expect(text.Ptr != nullptr && text.Len > 0, label); + expect(gen::str_contains(text, txt("baseline_message")), label); + expect(gen::str_contains(text, txt("char")), label); + expect(gen::str_contains(text, txt("const")), label); + expect(gen::str_contains(text, txt("*")), label); + expect(gen::str_contains(text, txt("GENCPP_BASELINE")), label); +} + +internal void expect_variable(gen::CodeVar var, gen::Str label) { + expect(var.ast != nullptr, label); if (var.ast == nullptr) return; + expect(var->Type == gen::CT_Variable, label); + expect(gen::str_contains(var->Name, txt("baseline_message")), label); + expect(var->ValueType.ast != nullptr, label); + expect(var->Value.ast != nullptr, label); + if (var->Value.ast != nullptr) { + expect(gen::str_contains(var->Value->Content, txt("GENCPP_BASELINE")), label); + } +} + int main() { using namespace gen; - Context ctx = {}; - gen::init( & ctx); + Context ctx = {}; init(&ctx); - Builder src_hello = builder_open("gen/hello.cpp"); - CodeVar hello_var = parse_variable( code( - constexpr char const* hello_gencpp_str = "HELLO GENCPP !"; - )); - src_hello.print(hello_var); - src_hello.write(); + Opts_def_type type_opts = {}; + type_opts.specifiers = def_specifiers(args(Spec_Const, Spec_Ptr)); + CodeTypename type = def_type(name(char), type_opts); + Opts_def_variable opts = {}; opts.value = untyped_str(code("\"GENCPP_BASELINE\"")); + CodeVar upfront = def_variable(type, name(baseline_message), opts); + expect_variable(upfront, txt("cpp upfront")); + expect_fragments(strbuilder_to_str(code_to_strbuilder((Code)upfront)), txt("cpp upfront serialized")); - CodeBody body = parse_file("gen/hello.cpp"); - for ( Code code : body ) switch (code->Type) { - case CT_Variable: - CodeVar var = cast(CodeVar, code); - log_fmt("%S", var->Value); - break; - } + Code untyped = code_str(char const* baseline_message = "GENCPP_BASELINE";); expect(untyped.ast != nullptr, txt("cpp untyped")); + Str untyped_text = strbuilder_to_str(code_to_strbuilder(untyped)); expect_fragments(untyped_text, txt("cpp untyped content")); + CodeVar parsed_untyped = parse_variable(untyped_text); expect_variable(parsed_untyped, txt("cpp untyped reparsed")); - gen::deinit(& ctx); + CodeVar parsed = parse_variable(code(char const* baseline_message = "GENCPP_BASELINE"; )); + expect_variable(parsed, txt("cpp parsed")); + expect_fragments(strbuilder_to_str(code_to_strbuilder((Code)parsed)), txt("cpp parsed serialized")); + + deinit(&ctx); + if (g_failures != 0) { log_fmt("cpp baseline smoke failed: %d\n", g_failures); return 1; } + log_fmt("cpp baseline smoke passed\n"); return 0; } diff --git a/test/verification/failure_assert.cpp b/test/verification/failure_assert.cpp new file mode 100644 index 0000000..60e68d7 --- /dev/null +++ b/test/verification/failure_assert.cpp @@ -0,0 +1,10 @@ +#define GEN_IMPLEMENTATION +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_ENFORCE_STRONG_CODE_TYPES +#include "gen.hpp" + +int main() +{ + gen::log_fmt("FAIL forced assertion\n"); + return 1; +} diff --git a/test/verification/failure_compile.cpp b/test/verification/failure_compile.cpp new file mode 100644 index 0000000..cdcfbed --- /dev/null +++ b/test/verification/failure_compile.cpp @@ -0,0 +1,3 @@ +int main( { + return 1; +} diff --git a/test/verification/failure_format.cmd b/test/verification/failure_format.cmd new file mode 100644 index 0000000..e77bca4 --- /dev/null +++ b/test/verification/failure_format.cmd @@ -0,0 +1,2 @@ +@echo off +exit /b 1 diff --git a/test/verification/failure_link.cpp b/test/verification/failure_link.cpp new file mode 100644 index 0000000..1bf54e7 --- /dev/null +++ b/test/verification/failure_link.cpp @@ -0,0 +1,7 @@ +extern "C" void baseline_missing_link_target(void); + +int main() +{ + baseline_missing_link_target(); + return 0; +} diff --git a/test/verification/failure_refactor.c b/test/verification/failure_refactor.c new file mode 100644 index 0000000..2cecef0 --- /dev/null +++ b/test/verification/failure_refactor.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 1; +} diff --git a/test/verification/stale_executable.c b/test/verification/stale_executable.c new file mode 100644 index 0000000..acc6892 --- /dev/null +++ b/test/verification/stale_executable.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 73; +}