<# .SYNOPSIS Teste standalone (fara Pester) pentru fluxul bin<->text VFP (FoxBin2Prg) si scriptul de write-back txt2vcx.ps1. Parametrizat per proiect (implicit ROAGEST). .DESCRIPTION Faze: - fidelity : esantion din FIECARE tip convertit, auto-descoperit din arborele proiectului. vcx/scx -> roundtrip complet bin->text1->bin->text2 (comparare pe octeti); frx/mnx/lbx/dbc/pjx/dbf -> doar validare bin->text (exit code + timeout + text nevid). Totul in temp, nimic scris in proiect. - edit : editare reala via txt2vcx.ps1 pe fixture-uri cunoscute (implicit ROAGEST); daca fixture-ul lipseste in proiect, se sare (warn). Restaurarea binarelor se face din backup temp (NU git checkout - binarele sunt git-ignored dupa migrare). - negative : text stricat, fisier gol, guard-uri (staleness, binar lipsa, cale in afara cache-ului, extensie nesuportata, COMUN, folder-foxbin2prg, -DryRun), test multi-fisier. Textul stricat ruleaza DOAR in cache temp, niciodata in arbore. - smoke : fidelity pe cate un .vcx din proiectele frate (-SmokeProjects). - all : toate cele de mai sus, in ordine. Restaurare: orice binar atins de write-back e copiat in backup temp INAINTE si restaurat la final (in finally), cu asertie byte-identic fata de original. Cache-ul text implicit e un director temporar creat pentru rulare (decuplat de arborele proiectului). Exit 1 daca orice Assert a esuat, CU EXCEPTIA celor marcate warn-only. .PARAMETER ProjectRoot Radacina proiectului principal testat (implicit ROAGEST). .PARAMETER CacheRoot Cache-ul text folosit de teste. Gol (implicit) => director temporar creat/sters de test, ca sa nu se scrie niciodata text in arborele proiectului. .PARAMETER Phase all | fidelity | edit | negative | smoke .PARAMETER SmokeProjects Lista proiectelor frate pentru faza smoke. #> [CmdletBinding()] param( [string]$ProjectRoot = 'D:\ROA\ROAGEST', [string]$CacheRoot = '', [ValidateSet('all','fidelity','edit','negative','smoke')] [string]$Phase = 'all', [string[]]$SmokeProjects = @('D:\ROA\ROAIMOB','D:\ROA\ROAACNPRO') ) $ErrorActionPreference = 'Stop' $exe = 'D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE' $vcx2txt = 'D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1' $txt2vcx = 'D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1' . (Join-Path $PSScriptRoot 'vfp_filemap.ps1') $script:Failures = @() $script:Enc = [Text.Encoding]::GetEncoding(1252) $script:BackupRoot = Join-Path $env:TEMP ("fb2p_test_backup_{0}_{1}" -f $PID, ([Guid]::NewGuid().ToString('N').Substring(0,8))) $script:Backups = @{} # cheie: cale binar (lower) -> info backup, pt restaurare la final function Assert([bool]$cond, [string]$msg) { if ($cond) { Write-Host " PASS: $msg" -ForegroundColor Green } else { Write-Host " FAIL: $msg" -ForegroundColor Red; $script:Failures += $msg } } function AssertWarn([bool]$cond, [string]$msg) { if ($cond) { Write-Host " PASS(warn-only): $msg" -ForegroundColor Green } else { Write-Warning " WARN (non-blocant): $msg" } } function Normalize-Crlf([byte[]]$bytes) { $out = New-Object System.Collections.Generic.List[byte] for ($i = 0; $i -lt $bytes.Length; $i++) { if ($bytes[$i] -eq 0x0D) { if ($i + 1 -lt $bytes.Length -and $bytes[$i+1] -eq 0x0A) { continue } $out.Add(0x0A) | Out-Null } else { $out.Add($bytes[$i]) | Out-Null } } return $out.ToArray() } function Test-BytesEqual([byte[]]$a, [byte[]]$b) { $na = Normalize-Crlf $a; $nb = Normalize-Crlf $b if ($na.Length -ne $nb.Length) { return $false } for ($i = 0; $i -lt $na.Length; $i++) { if ($na[$i] -ne $nb[$i]) { return $false } } return $true } function Invoke-Fb2p { # Watchdog obligatoriu - FoxBin2Prg poate afisa un MessageBox modal nativ VFP la text stricat; # fara asta, faza negativa ar ramane agatata la infinit intr-o rulare automata. param([string]$InputFile, [string]$Recompile = '', [int]$TimeoutSeconds = 60) # cfg in dir-ul input-ului: DontShowErrors suprima MessageBox-ul propriu FoxBin2Prg (ca in git_sync) $cfgLines = [System.Collections.Generic.List[string]]@('DontShowErrors: 1','ShowProgressbar: 0') if ([IO.Path]::GetExtension($InputFile).ToLower() -eq '.dbf') { $cfgLines.Add('DBF_Conversion_Support: 4') } Set-Content -LiteralPath (Join-Path (Split-Path -Parent $InputFile) 'foxbin2prg.cfg') -Value $cfgLines -Encoding Ascii if ($Recompile) { $r = Invoke-FoxBin2PrgSafe -ExePath $exe -ExeArgs @($InputFile,'','','','1','0','1','',$Recompile) -TimeoutSeconds $TimeoutSeconds } else { $r = Invoke-FoxBin2PrgSafe -ExePath $exe -ExeArgs @($InputFile,'','','','1','0','1') -TimeoutSeconds $TimeoutSeconds } if ($r.TimedOut) { Write-Warning "Invoke-Fb2p: timeout pe '$InputFile' - tratat ca ESEC." } return $r.ExitCode } function Get-FileHashBytes([string]$path) { if (-not (Test-Path -LiteralPath $path)) { return $null } return (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash } function Get-RelPath([string]$Root, [string]$Full) { return $Full.Substring($Root.TrimEnd('\').Length).TrimStart('\') } # --- Auto-descoperire: un esantion mic non-COMUN de tipul cerut, cu memo companion --- function Find-SampleBinary { param( [Parameter(Mandatory)][string]$ProjRoot, [Parameter(Mandatory)][string]$Ext, [bool]$RequireMemo = $true, [int]$MaxBytes = 500000 ) if (-not (Test-Path -LiteralPath $ProjRoot)) { return $null } $memoExt = $script:VfpFileMap[$Ext][0] Get-ChildItem -LiteralPath $ProjRoot -Recurse -File -Filter ("*" + $Ext) -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '(?i)\\COMUN\\' -and $_.Length -le $MaxBytes -and $_.Length -gt 0 -and (-not $RequireMemo -or (Test-Path -LiteralPath ([IO.Path]::ChangeExtension($_.FullName, $memoExt)))) } | Sort-Object Length | Select-Object -First 1 } # --- Backup/restore binare din temp (inlocuieste git checkout - binarele sunt git-ignored) --- function Backup-ProjectBinary([string]$fullBin) { $key = $fullBin.ToLower() if ($script:Backups.ContainsKey($key)) { return } $ext = [IO.Path]::GetExtension($fullBin).ToLower() $memoExt = $script:VfpFileMap[$ext][0] $dir = Split-Path $fullBin -Parent $base = [IO.Path]::GetFileNameWithoutExtension($fullBin) $memoEntry = Get-ChildItem -LiteralPath $dir -Filter ($base + $memoExt) -ErrorAction SilentlyContinue | Select-Object -First 1 $memoFull = if ($memoEntry) { $memoEntry.FullName } else { [IO.Path]::ChangeExtension($fullBin, $memoExt) } $bdir = Join-Path $script:BackupRoot ([Guid]::NewGuid().ToString('N').Substring(0,8)) New-Item -ItemType Directory -Force -Path $bdir | Out-Null $binBak = Join-Path $bdir (Split-Path $fullBin -Leaf) Copy-Item -LiteralPath $fullBin -Destination $binBak -Force $memoBak = $null if (Test-Path -LiteralPath $memoFull) { $memoBak = Join-Path $bdir (Split-Path $memoFull -Leaf) Copy-Item -LiteralPath $memoFull -Destination $memoBak -Force } $script:Backups[$key] = @{ BinFull=$fullBin; BinBak=$binBak; MemoFull=$memoFull; MemoBak=$memoBak; OrigHash=(Get-FileHashBytes $fullBin) } } function Restore-ProjectBinaries { foreach ($k in @($script:Backups.Keys)) { $b = $script:Backups[$k] Copy-Item -LiteralPath $b.BinBak -Destination $b.BinFull -Force if ($b.MemoBak) { Copy-Item -LiteralPath $b.MemoBak -Destination $b.MemoFull -Force } $now = Get-FileHashBytes $b.BinFull Assert ($now -eq $b.OrigHash) "Restore: $(Split-Path $b.BinFull -Leaf) byte-identic cu originalul dupa restore" } } # --- Fidelitate: roundtrip complet bin->text1->bin->text2 (doar in temp) - pt vcx/scx --- function Test-FidelityRoundtrip { param( [Parameter(Mandatory)][string]$RelPath, [Parameter(Mandatory)][string]$ProjRoot, [bool]$WarnOnly = $false ) $full = Join-Path $ProjRoot $RelPath if (-not (Test-Path -LiteralPath $full)) { AssertWarn $false "Faza A: fisier lipsa, sarit: $RelPath"; return } $ext = [IO.Path]::GetExtension($full).ToLower() $pair = $script:VfpFileMap[$ext] if (-not $pair) { AssertWarn $false "Faza A: extensie necunoscuta, sarit: $RelPath"; return } $memoExt = $pair[0]; $txtExt = $pair[1] $memoFull = [IO.Path]::ChangeExtension($full, $memoExt) $work = Join-Path $env:TEMP ("fb2p_fidelity_{0}_{1}" -f $PID, ([Guid]::NewGuid().ToString('N').Substring(0,8))) New-Item -ItemType Directory -Force -Path $work | Out-Null try { $baseName = [IO.Path]::GetFileNameWithoutExtension($full) $bin1 = Join-Path $work ($baseName + $ext); $memo1 = Join-Path $work ($baseName + $memoExt) Copy-Item -LiteralPath $full -Destination $bin1 -Force if (Test-Path -LiteralPath $memoFull) { Copy-Item -LiteralPath $memoFull -Destination $memo1 -Force } $rc1 = Invoke-Fb2p -InputFile $bin1 $text1 = Join-Path $work ($baseName + $txtExt) if ($rc1 -ne 0 -or -not (Test-Path -LiteralPath $text1)) { if ($WarnOnly) { AssertWarn $false "Faza A ($RelPath): Bin2Prg #1 a esuat (ErrorLevel=$rc1)"; return } Assert $false "Faza A ($RelPath): Bin2Prg #1 a esuat (ErrorLevel=$rc1)" return } $stage2 = Join-Path $work 'stage2' New-Item -ItemType Directory -Force -Path $stage2 | Out-Null $text1copy = Join-Path $stage2 ($baseName + $txtExt) Copy-Item -LiteralPath $text1 -Destination $text1copy -Force $rc2 = Invoke-Fb2p -InputFile $text1copy -Recompile $ProjRoot $bin2 = Join-Path $stage2 ($baseName + $ext); $memo2 = Join-Path $stage2 ($baseName + $memoExt) if ($rc2 -ne 0 -or -not (Test-Path -LiteralPath $bin2) -or -not (Test-Path -LiteralPath $memo2)) { $ok = $false } else { $rc3 = Invoke-Fb2p -InputFile $bin2 $text2 = Join-Path $stage2 ($baseName + $txtExt) $ok = ($rc3 -eq 0) -and (Test-Path -LiteralPath $text2) -and (Test-BytesEqual ([IO.File]::ReadAllBytes($text1)) ([IO.File]::ReadAllBytes($text2))) } if ($WarnOnly) { AssertWarn $ok "Faza A ($RelPath): text1 == text2 dupa roundtrip complet" } else { Assert $ok "Faza A ($RelPath): text1 == text2 dupa roundtrip complet" } } finally { Remove-Item -Recurse -Force -LiteralPath $work -ErrorAction SilentlyContinue } } # --- Validare bin->text (fara roundtrip) - pt frx/mnx/lbx/dbc/pjx/dbf (write-back nesuportat) --- function Test-BinToTextValid { param( [Parameter(Mandatory)][string]$RelPath, [Parameter(Mandatory)][string]$ProjRoot, [bool]$WarnOnly = $false ) $full = Join-Path $ProjRoot $RelPath if (-not (Test-Path -LiteralPath $full)) { AssertWarn $false "Faza A: fisier lipsa, sarit: $RelPath"; return } $ext = [IO.Path]::GetExtension($full).ToLower() $pair = $script:VfpFileMap[$ext] if (-not $pair) { AssertWarn $false "Faza A: extensie necunoscuta, sarit: $RelPath"; return } $memoExt = $pair[0]; $txtExt = $pair[1] $memoFull = [IO.Path]::ChangeExtension($full, $memoExt) $work = Join-Path $env:TEMP ("fb2p_valid_{0}_{1}" -f $PID, ([Guid]::NewGuid().ToString('N').Substring(0,8))) New-Item -ItemType Directory -Force -Path $work | Out-Null try { $baseName = [IO.Path]::GetFileNameWithoutExtension($full) $bin1 = Join-Path $work ($baseName + $ext) Copy-Item -LiteralPath $full -Destination $bin1 -Force if (Test-Path -LiteralPath $memoFull) { Copy-Item -LiteralPath $memoFull -Destination (Join-Path $work ($baseName + $memoExt)) -Force } $rc = Invoke-Fb2p -InputFile $bin1 $text1 = Join-Path $work ($baseName + $txtExt) $ok = ($rc -eq 0) -and (Test-Path -LiteralPath $text1) -and ((Get-Item -LiteralPath $text1).Length -gt 0) $msg = "Faza A ($RelPath): bin->text valid (exit=$rc, text nevid)" if ($WarnOnly) { AssertWarn $ok $msg } else { Assert $ok $msg } } finally { Remove-Item -Recurse -Force -LiteralPath $work -ErrorAction SilentlyContinue } } # specificatie per tip convertit: roundtrip doar vcx/scx; restul doar validare bin->text $script:FidelitySpec = @( @{ Ext='.vcx'; Roundtrip=$true; WarnOnly=$false; RequireMemo=$true } @{ Ext='.scx'; Roundtrip=$true; WarnOnly=$false; RequireMemo=$true } @{ Ext='.frx'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$true } @{ Ext='.mnx'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$true } @{ Ext='.lbx'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$true } @{ Ext='.dbc'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$true } @{ Ext='.pjx'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$true } @{ Ext='.dbf'; Roundtrip=$false; WarnOnly=$true; RequireMemo=$false } ) function Invoke-FidelityForProject([string]$ProjRoot) { foreach ($s in $script:FidelitySpec) { $sample = Find-SampleBinary -ProjRoot $ProjRoot -Ext $s.Ext -RequireMemo $s.RequireMemo if (-not $sample) { AssertWarn $false "Faza A: niciun esantion $($s.Ext) in $ProjRoot, sarit"; continue } $rel = Get-RelPath $ProjRoot $sample.FullName Write-Host " esantion $($s.Ext): $rel ($([int]($sample.Length/1024)) KB)" if ($s.Roundtrip) { Test-FidelityRoundtrip -RelPath $rel -ProjRoot $ProjRoot -WarnOnly $s.WarnOnly } else { Test-BinToTextValid -RelPath $rel -ProjRoot $ProjRoot -WarnOnly $s.WarnOnly } } } # --- Editare reala prin txt2vcx.ps1, cu backup/restore (nu git) --- function Test-EditRoundtrip { param( [Parameter(Mandatory)][string]$RelPath, [Parameter(Mandatory)][string]$CaptionOld, [Parameter(Mandatory)][string]$CaptionNew, [Parameter(Mandatory)][string]$ProcedureMarker ) $full = Join-Path $ProjectRoot $RelPath if (-not (Test-Path -LiteralPath $full)) { AssertWarn $false "Faza B: fixture lipsa in proiect, sarit: $RelPath"; return } $ext = [IO.Path]::GetExtension($RelPath).ToLower() $pair = $script:VfpFileMap[$ext] $txtExt = $pair[1] $relDir = Split-Path $RelPath -Parent $base = [IO.Path]::GetFileNameWithoutExtension($RelPath) $relText = if ($relDir) { Join-Path $relDir ($base + $txtExt) } else { $base + $txtExt } Write-Host "-- Faza B: $RelPath --" Backup-ProjectBinary $full $origHash = $script:Backups[$full.ToLower()].OrigHash & $vcx2txt -Source $full -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -Force | Out-Null $cacheText = Join-Path $CacheRoot $relText Assert (Test-Path -LiteralPath $cacheText) "Faza B ($RelPath): cache text regenerat" $text = $script:Enc.GetString([IO.File]::ReadAllBytes($cacheText)) Assert ($text.Contains($CaptionOld)) "Faza B ($RelPath): linia Caption originala gasita" $text = $text.Replace($CaptionOld, $CaptionNew) $marker = "`t" + $ProcedureMarker + "`r`n" $idx = $text.IndexOf($marker) Assert ($idx -ge 0) "Faza B ($RelPath): marker '$ProcedureMarker' gasit" if ($idx -ge 0) { $insertPos = $idx + $marker.Length $text = $text.Substring(0,$insertPos) + "`t`t* test roundtrip`r`n" + $text.Substring($insertPos) } [IO.File]::WriteAllBytes($cacheText, $script:Enc.GetBytes($text)) & $txt2vcx -TextFile $cacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot $rc = $LASTEXITCODE Assert ($rc -eq 0) "Faza B ($RelPath): txt2vcx.ps1 exit 0" Assert ((Get-FileHashBytes $full) -ne $origHash) "Faza B ($RelPath): binarul din proiect a fost rescris (hash diferit de original)" $bakStray = Join-Path (Join-Path $ProjectRoot $relDir) ($base + '.bak') Assert (-not (Test-Path -LiteralPath $bakStray)) "Faza B ($RelPath): fara .bak lasat in proiect" $regenText = $script:Enc.GetString([IO.File]::ReadAllBytes($cacheText)) Assert ($regenText.Contains($CaptionNew)) "Faza B ($RelPath): textul regenerat contine Caption editat" Assert ($regenText.Contains('* test roundtrip')) "Faza B ($RelPath): textul regenerat contine comentariul adaugat" # binarul e restaurat din backup in finally-ul global } # ============================== MAIN ============================== $ownCache = $false if ([string]::IsNullOrWhiteSpace($CacheRoot)) { $CacheRoot = Join-Path $env:TEMP ("fb2p_test_cache_{0}_{1}" -f $PID, ([Guid]::NewGuid().ToString('N').Substring(0,8))) $ownCache = $true } if (-not (Test-Path -LiteralPath $CacheRoot)) { New-Item -ItemType Directory -Force -Path $CacheRoot | Out-Null } New-Item -ItemType Directory -Force -Path $script:BackupRoot | Out-Null Write-Host "=== test_roundtrip.ps1 -- ProjectRoot=$ProjectRoot CacheRoot=$CacheRoot Phase=$Phase ===" # fixture-uri de editare cunoscute (implicit ROAGEST); se sar automat daca lipsesc in proiect $editFixtures = @( @{ Rel='Clase\oavize.vcx'; Old='Lb_titlu_alb_b121.Caption = "Date aviz"'; New='Lb_titlu_alb_b121.Caption = "Atentie-diacritice-ok"'; Marker='PROCEDURE Init' } @{ Rel='Ferestre\fundal.scx'; Old='Caption = ""'; New='Caption = "test"'; Marker='PROCEDURE Show' } ) try { if ($Phase -eq 'all' -or $Phase -eq 'fidelity') { Write-Host "`n--- FAZA A: fidelitate (esantion din fiecare tip convertit) ---" Invoke-FidelityForProject $ProjectRoot } if ($Phase -eq 'all' -or $Phase -eq 'edit') { Write-Host "`n--- FAZA B: editare reala (write-back) ---" foreach ($fx in $editFixtures) { Test-EditRoundtrip -RelPath $fx.Rel -CaptionOld $fx.Old -CaptionNew $fx.New -ProcedureMarker $fx.Marker } } if ($Phase -eq 'all' -or $Phase -eq 'smoke') { Write-Host "`n--- FAZA C: smoke pe proiecte frate ---" foreach ($sp in $SmokeProjects) { if (-not (Test-Path -LiteralPath $sp)) { AssertWarn $false "Faza C: proiect lipsa $sp, sarit"; continue } $s = Find-SampleBinary -ProjRoot $sp -Ext '.vcx' if ($s) { Test-FidelityRoundtrip -RelPath (Get-RelPath $sp $s.FullName) -ProjRoot $sp } else { AssertWarn $false "Faza C: niciun .vcx in $sp, sarit" } } } if ($Phase -eq 'all' -or $Phase -eq 'negative') { Write-Host "`n--- FAZA D: negativ + guard-uri + multi-fisier ---" $vcxSample = Find-SampleBinary -ProjRoot $ProjectRoot -Ext '.vcx' if (-not $vcxSample) { AssertWarn $false "Faza D: niciun .vcx in $ProjectRoot, faza sarita" } else { $vcxRel = Get-RelPath $ProjectRoot $vcxSample.FullName $vcxFull = $vcxSample.FullName $vcxDir = Split-Path $vcxRel -Parent $vcxBase = [IO.Path]::GetFileNameWithoutExtension($vcxRel) $vcxRelText = if ($vcxDir) { Join-Path $vcxDir ($vcxBase + '.vc2') } else { $vcxBase + '.vc2' } Write-Host " esantion negativ .vcx: $vcxRel" Backup-ProjectBinary $vcxFull $origVcxHash = Get-FileHashBytes $vcxFull # baseline curent (independent de backup) pt asertii "neatins" & $vcx2txt -Source $vcxFull -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -Force | Out-Null $cacheText = Join-Path $CacheRoot $vcxRelText Assert (Test-Path -LiteralPath $cacheText) "Faza D: text esantion generat pentru $vcxRel" $origBytes = [IO.File]::ReadAllBytes($cacheText) # (a) text stricat: adauga o clasa neinchisa -> recompilare esueaza (doar in cache temp) $text = $script:Enc.GetString($origBytes) $brokenSuffix = "`r`nDEFINE CLASS _brokenzzz AS Custom`r`n" [IO.File]::WriteAllBytes($cacheText, $script:Enc.GetBytes($text + $brokenSuffix)) & $txt2vcx -TextFile $cacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -ne 0) "Faza D (a): txt2vcx exit != 0 pe text stricat (DEFINE CLASS neinchis)" Assert ((Get-FileHashBytes $vcxFull) -eq $origVcxHash) "Faza D (a): binarul esantion neatins in proiect" [IO.File]::WriteAllBytes($cacheText, $origBytes) # (b) fisier text gol [IO.File]::WriteAllBytes($cacheText, [byte[]]@()) & $txt2vcx -TextFile $cacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -ne 0) "Faza D (b): txt2vcx exit != 0 pe fisier text gol" Assert ((Get-FileHashBytes $vcxFull) -eq $origVcxHash) "Faza D (b): binarul esantion neatins in proiect" [IO.File]::WriteAllBytes($cacheText, $origBytes) # (guard) staleness: binar mai nou decat textul -> refuz fara -Force $origMtime = (Get-Item -LiteralPath $vcxFull).LastWriteTime (Get-Item -LiteralPath $vcxFull).LastWriteTime = (Get-Date).AddDays(1) try { [IO.File]::WriteAllBytes($cacheText, $origBytes) & $txt2vcx -TextFile $cacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -ne 0) "Faza D (guard staleness): refuz fara -Force cand binarul e mai nou" Assert ((Get-FileHashBytes $vcxFull) -eq $origVcxHash) "Faza D (guard staleness): binar neatins" } finally { (Get-Item -LiteralPath $vcxFull).LastWriteTime = $origMtime } # (guard) binar tinta lipsa -> refuz PERMANENT (si cu -Force) $missingCacheText = Join-Path $CacheRoot $(if ($vcxDir) { Join-Path $vcxDir '_nu_exista_deloc.vc2' } else { '_nu_exista_deloc.vc2' }) New-Item -ItemType Directory -Force -Path (Split-Path $missingCacheText -Parent) | Out-Null [IO.File]::WriteAllBytes($missingCacheText, $script:Enc.GetBytes("* fake`r`nDEFINE CLASS x AS y`r`nENDDEFINE`r`n")) try { & $txt2vcx -TextFile $missingCacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -Force Assert ($LASTEXITCODE -ne 0) "Faza D (guard binar lipsa): refuz chiar si cu -Force" } finally { Remove-Item -LiteralPath $missingCacheText -Force -ErrorAction SilentlyContinue } # (guard) cale text in afara CacheRoot -> refuz $outsideText = Join-Path $env:TEMP ('_outside_' + [Guid]::NewGuid().ToString('N').Substring(0,8) + '.vc2') [IO.File]::WriteAllBytes($outsideText, $origBytes) try { & $txt2vcx -TextFile $outsideText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -ne 0) "Faza D (guard cale in afara CacheRoot): refuz" } finally { Remove-Item -LiteralPath $outsideText -Force -ErrorAction SilentlyContinue } # (guard) extensie nesuportata (.mn2) $unsupported = Join-Path $CacheRoot $(if ($vcxDir) { Join-Path $vcxDir '_fake.mn2' } else { '_fake.mn2' }) [IO.File]::WriteAllBytes($unsupported, $script:Enc.GetBytes("* fake mn2`r`n")) try { & $txt2vcx -TextFile $unsupported -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -ne 0) "Faza D (guard extensie nesuportata .mn2): refuz" } finally { Remove-Item -LiteralPath $unsupported -Force -ErrorAction SilentlyContinue } # (guard) COMUN fara -AllowComun -> refuz (-DryRun, zero scrieri) $comunText = Join-Path $CacheRoot 'COMUN\clase\_fake.vc2' New-Item -ItemType Directory -Force -Path (Split-Path $comunText -Parent) | Out-Null [IO.File]::WriteAllBytes($comunText, $origBytes) try { & $txt2vcx -TextFile $comunText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -DryRun Assert ($LASTEXITCODE -ne 0) "Faza D (guard COMUN, fara -AllowComun): refuz" } finally { Remove-Item -Recurse -Force -LiteralPath (Split-Path $comunText -Parent) -ErrorAction SilentlyContinue } # (guard) folder-foxbin2prg -> refuz necondiționat (-DryRun) $fb2pCache = Join-Path $env:TEMP ('_fb2pguard_' + [Guid]::NewGuid().ToString('N').Substring(0,8)) New-Item -ItemType Directory -Force -Path $fb2pCache | Out-Null $fb2pText = Join-Path $fb2pCache 'fake.vc2' [IO.File]::WriteAllBytes($fb2pText, $origBytes) try { & $txt2vcx -TextFile $fb2pText -ProjectRoot 'D:\ROA\UTIL\foxbin2prg' -CacheRoot $fb2pCache -DryRun Assert ($LASTEXITCODE -ne 0) "Faza D (guard folder-foxbin2prg): refuz necondiționat" } finally { Remove-Item -Recurse -Force -LiteralPath $fb2pCache -ErrorAction SilentlyContinue } # (-DryRun caz valid): nimic modificat in proiect, exit 0 [IO.File]::WriteAllBytes($cacheText, $origBytes) & $txt2vcx -TextFile $cacheText -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -DryRun Assert ($LASTEXITCODE -eq 0) "Faza D (-DryRun, caz valid): exit 0" Assert ((Get-FileHashBytes $vcxFull) -eq $origVcxHash) "Faza D (-DryRun, caz valid): binar neatins in proiect" # --- multi-fisier: un scx valid (scris) + vcx stricat (refuzat) in aceeasi rulare --- $scxSample = Find-SampleBinary -ProjRoot $ProjectRoot -Ext '.scx' if ($scxSample) { $scxRel = Get-RelPath $ProjectRoot $scxSample.FullName $scxFull = $scxSample.FullName $scxDir = Split-Path $scxRel -Parent $scxBase = [IO.Path]::GetFileNameWithoutExtension($scxRel) $scxRelText = if ($scxDir) { Join-Path $scxDir ($scxBase + '.sc2') } else { $scxBase + '.sc2' } Backup-ProjectBinary $scxFull $scxOrigMtime = (Get-Item -LiteralPath $scxFull).LastWriteTime & $vcx2txt -Source $scxFull -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot -Force | Out-Null $scxCacheText = Join-Path $CacheRoot $scxRelText $scxText = $script:Enc.GetString([IO.File]::ReadAllBytes($scxCacheText)) $m = [regex]::Match($scxText, "(?m)^\tPROCEDURE \w+\r?\n") if ($m.Success) { $pos = $m.Index + $m.Length $scxText = $scxText.Substring(0,$pos) + "`t`t* test multi`r`n" + $scxText.Substring($pos) [IO.File]::WriteAllBytes($scxCacheText, $script:Enc.GetBytes($scxText)) } [IO.File]::WriteAllBytes($cacheText, $script:Enc.GetBytes($text + $brokenSuffix)) & $txt2vcx -TextFile @($scxCacheText, $cacheText) -ProjectRoot $ProjectRoot -CacheRoot $CacheRoot Assert ($LASTEXITCODE -eq 1) "Faza D (multi-fisier): exit agregat = 1 (unul din doua a esuat)" Assert ((Get-FileHashBytes $vcxFull) -eq $origVcxHash) "Faza D (multi-fisier): vcx stricat neatins in proiect" Assert (((Get-Item -LiteralPath $scxFull).LastWriteTime) -ne $scxOrigMtime) "Faza D (multi-fisier): scx valid a fost scris in proiect (mtime schimbat)" [IO.File]::WriteAllBytes($cacheText, $origBytes) } else { AssertWarn $false "Faza D (multi-fisier): niciun .scx in $ProjectRoot, sarit" } } } } finally { Write-Host "`n--- Cleanup final: restore binare din backup + curatare temp ---" Restore-ProjectBinaries if ($ownCache -and (Test-Path -LiteralPath $CacheRoot)) { Remove-Item -Recurse -Force -LiteralPath $CacheRoot -ErrorAction SilentlyContinue } if (Test-Path -LiteralPath $script:BackupRoot) { Remove-Item -Recurse -Force -LiteralPath $script:BackupRoot -ErrorAction SilentlyContinue } } Write-Host "`n=== REZULTAT ===" if ($script:Failures.Count -eq 0) { Write-Host "TOATE testele au trecut (Phase=$Phase)." -ForegroundColor Green exit 0 } else { Write-Host "$($script:Failures.Count) test(e) esuate:" -ForegroundColor Red $script:Failures | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } exit 1 }