Initial: flux text FoxBin2Prg (git urmareste .??2 in-arbore, binarele VFP git-ignored)
Include si sistemul de depanare/testare headless din Teste\ (harness UI cu capturi, verificare de sintaxa prin compilare, wrappere text<->binar) si docs\depanare_testare_flora2roa.md.
This commit is contained in:
66
Teste/_precompile.ps1
Normal file
66
Teste/_precompile.ps1
Normal file
@@ -0,0 +1,66 @@
|
||||
# _precompile.ps1 -Prg <cale.prg> - regenereaza .fxp-ul target intr-un proces IZOLAT.
|
||||
# De ce izolat: precompilarea (vfp9 -A ... COMPILE) atarna dupa COMPILE si e omorata; daca
|
||||
# ruleaza in ACEEASI sesiune powershell care lanseaza apoi testul, lansarile ulterioare de
|
||||
# vfp9 esueaza (deschid editorul in loc sa ruleze). Rulat ca proces separat (Start-Process
|
||||
# -Wait din harness), nu otraveste sesiunea care lanseaza testul.
|
||||
param([Parameter(Mandatory=$true)][string]$Prg)
|
||||
$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
||||
$Fxp = [System.IO.Path]::ChangeExtension($Prg, 'fxp')
|
||||
$dir = [System.IO.Path]::GetDirectoryName($Prg)
|
||||
$tmpPrg = Join-Path $dir '_compile_tmp.prg'
|
||||
# prg temporar care compileaza target-ul (COMPILE cu variabila; forma cu literal in ghilimele da eroare)
|
||||
Set-Content -Path $tmpPrg -Value ("lcT = `"$Prg`"`r`nCOMPILE (lcT)`r`nQUIT`r`n") -Encoding ascii
|
||||
# omoara doar vfp9 proprii (linia de comanda contine folderul testului), nu instante straine
|
||||
function Stop-VfpProprii([string]$Marker) {
|
||||
Get-CimInstance Win32_Process -Filter "Name='vfp9.exe'" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
if ($_.CommandLine -and $_.CommandLine.ToLower().Contains($Marker.ToLower())) {
|
||||
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
Stop-VfpProprii $dir
|
||||
Start-Sleep -Milliseconds 500
|
||||
if (Test-Path $Fxp) { [System.IO.File]::Delete($Fxp) }
|
||||
|
||||
# Fara furt de focus: fereastra vfp9 a compilarii (chiar tranzitorie) e impinsa off-screen,
|
||||
# la fel ca in vfp_ui_harness.ps1 (SetWindowPos HWND_BOTTOM + NOACTIVATE, x=-4000).
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class Win32UiPc {
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool IsWindow(IntPtr hWnd);
|
||||
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
||||
public const uint SWP_NOSIZE = 0x0001;
|
||||
public const uint SWP_NOACTIVATE = 0x0010;
|
||||
}
|
||||
"@
|
||||
function Push-OffscreenPc([IntPtr]$Hwnd) {
|
||||
if ($Hwnd -eq [IntPtr]::Zero -or -not [Win32UiPc]::IsWindow($Hwnd)) { return }
|
||||
[Win32UiPc]::SetWindowPos($Hwnd, [Win32UiPc]::HWND_BOTTOM, -4000, 0, 0, 0, ([Win32UiPc]::SWP_NOSIZE -bor [Win32UiPc]::SWP_NOACTIVATE)) | Out-Null
|
||||
}
|
||||
|
||||
$ok = $false
|
||||
foreach ($try in 1..3) {
|
||||
$pcProc = Start-Process -FilePath $Vfp -ArgumentList @('-A', "`"$tmpPrg`"") -PassThru
|
||||
$dl = (Get-Date).AddSeconds(30)
|
||||
$pcHwnd = [IntPtr]::Zero
|
||||
while (-not (Test-Path $Fxp) -and (Get-Date) -lt $dl) {
|
||||
if ($pcHwnd -eq [IntPtr]::Zero -and -not $pcProc.HasExited) {
|
||||
$pcProc.Refresh()
|
||||
if ($pcProc.MainWindowHandle -ne [IntPtr]::Zero) { $pcHwnd = $pcProc.MainWindowHandle }
|
||||
}
|
||||
Push-OffscreenPc $pcHwnd
|
||||
Start-Sleep -Milliseconds 300
|
||||
}
|
||||
Start-Sleep -Seconds 3
|
||||
Stop-VfpProprii $dir
|
||||
Start-Sleep -Seconds 1
|
||||
if (Test-Path $Fxp) { $ok = $true; break }
|
||||
}
|
||||
foreach ($f in @($tmpPrg, [System.IO.Path]::ChangeExtension($tmpPrg, 'fxp'))) {
|
||||
if (Test-Path $f) { try { [System.IO.File]::Delete($f) } catch {} }
|
||||
}
|
||||
if ($ok) { exit 0 } else { exit 1 }
|
||||
49
Teste/bin_din_text.ps1
Normal file
49
Teste/bin_din_text.ps1
Normal file
@@ -0,0 +1,49 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Scrie inapoi in proiect binarele VFP (.scx+.sct / .vcx+.vct) regenerate din textul editat.
|
||||
|
||||
.DESCRIPTION
|
||||
Wrapper peste D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1 cu parametrii FLORA2ROA. Flux in-arbore:
|
||||
textul .sc2/.vc2 sta LANGA binar, deci -CacheRoot = radacina proiectului.
|
||||
|
||||
Regenereaza in staging, face fidelity check (bin -> text, comparat pe octeti) si abia apoi
|
||||
copiaza peste binarul din proiect. Refuza daca binarul e blocat (IDE sau flora2roa.exe
|
||||
deschis) sau daca e mai nou decat textul (fara -Force).
|
||||
|
||||
Editarea textului se face byte-safe, in Windows-1252:
|
||||
$t = [IO.File]::ReadAllText($p, [Text.Encoding]::GetEncoding(1252))
|
||||
[IO.File]::WriteAllText($p, $t, [Text.Encoding]::GetEncoding(1252))
|
||||
NU cu tool-uri care scriu UTF-8: corup diacriticele din TOT fisierul.
|
||||
Vezi COMUN\docs\flux-editare-vfp-text.md si conventie_encoding_cp1252.md.
|
||||
|
||||
Dupa write-back binarul e modificat in SVN: se comite in SVN doar cu acordul utilizatorului.
|
||||
|
||||
.PARAMETER TextFile
|
||||
Unul sau mai multe fisiere .sc2/.vc2 din arbore (ex. frm_import.sc2).
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile frm_import.sc2 -DryRun
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile frm_import.sc2
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string[]]$TextFile,
|
||||
[string]$ProjectRoot,
|
||||
[switch]$Force,
|
||||
[switch]$NoVerify,
|
||||
[switch]$DryRun
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent }
|
||||
$tool = 'D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1'
|
||||
if (-not (Test-Path $tool)) { throw "Nu gasesc $tool (tooling-ul partajat FoxBin2Prg)." }
|
||||
|
||||
$TextFile = $TextFile | ForEach-Object { if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $ProjectRoot $_ } }
|
||||
|
||||
# flux in-arbore: CacheRoot = ProjectRoot (maparea text->binar devine identitate)
|
||||
& $tool -TextFile $TextFile -ProjectRoot $ProjectRoot -CacheRoot $ProjectRoot `
|
||||
-Force:$Force -NoVerify:$NoVerify -DryRun:$DryRun
|
||||
exit $LASTEXITCODE
|
||||
21
Teste/mock_amessagebox.prg
Normal file
21
Teste/mock_amessagebox.prg
Normal file
@@ -0,0 +1,21 @@
|
||||
* mock_amessagebox.prg - inlocuieste FUNCTION amessagebox (myutils.prg) in testele headless,
|
||||
* ca dialogurile modale sa nu blocheze procesul. Returneaza 6 (Da/OK) implicit.
|
||||
* Se incarca cu SET PROCEDURE ... ADDITIVE dupa initializarea mediului: la nume duplicat de
|
||||
* procedura VFP alege fisierul deschis cel mai recent.
|
||||
* gnMockRaspuns (PUBLIC, optional): valoarea returnata (implicit 6).
|
||||
* gcMockUltimMesaj / gnMockUltimTip (PUBLIC, optional): daca testul declara gcMockUltimMesaj ca
|
||||
* sir, mock-ul memoreaza mesajul si tipul ultimului apel, pentru aserturi pe textul dialogului.
|
||||
FUNCTION amessagebox
|
||||
LPARAMETERS tcMessage, tnDialogBoxType, tcTitle, tcFont, tnTimeOut, tnTimeoutValue
|
||||
LOCAL lnRaspuns
|
||||
lnRaspuns = IIF(TYPE('gnMockRaspuns') = 'N', gnMockRaspuns, 6)
|
||||
IF TYPE('gcMockUltimMesaj') = 'C'
|
||||
gcMockUltimMesaj = TRANSFORM(tcMessage)
|
||||
gnMockUltimTip = IIF(TYPE('tnDialogBoxType') = 'N', tnDialogBoxType, 0)
|
||||
ENDIF
|
||||
TestLog('MOCK amessagebox: [' + ALLTRIM(TRANSFORM(tcMessage)) + '] -> returnez ' + TRANSFORM(lnRaspuns))
|
||||
RETURN lnRaspuns
|
||||
ENDFUNC
|
||||
|
||||
* MESSAGEBOX nativ nu se poate umbri; formularul apeleaza si MESSAGEBOX() direct in cateva
|
||||
* locuri. Testele care ating acele ramuri trebuie sa evite conditia care le declanseaza.
|
||||
74
Teste/ruleaza_teste.ps1
Normal file
74
Teste/ruleaza_teste.ps1
Normal file
@@ -0,0 +1,74 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Ruleaza suita de teste FLORA2ROA (verificare de sintaxa + teste UI cu capturi).
|
||||
|
||||
.DESCRIPTION
|
||||
Ordinea e cea din COMUN\docs\depanare_testare_vfp.md: intai compilarea de verificare (ieftina,
|
||||
prinde sintaxa), apoi testele functionale pe fluxul real al utilizatorului.
|
||||
|
||||
Suitele NU se ruleaza in paralel: harness-ul isi omoara instantele vfp9 proprii inainte de
|
||||
fiecare lansare (filtrat pe linia de comanda, instantele straine nu sunt atinse).
|
||||
|
||||
.PARAMETER Doar
|
||||
Ruleaza numai testele al caror nume contine acest sir.
|
||||
|
||||
.PARAMETER FaraSintaxa
|
||||
Sare peste verificarea de sintaxa.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\ruleaza_teste.ps1
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\ruleaza_teste.ps1 -Doar export
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Doar,
|
||||
[switch]$FaraSintaxa
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Teste = $PSScriptRoot
|
||||
$Root = Split-Path $Teste -Parent
|
||||
|
||||
# nume test -> etichetele pasilor asteptate de vfp_ui_harness.ps1 (o captura per pas)
|
||||
$suita = @(
|
||||
@{ Prg = 'test_frm_import_ui.prg'; Steps = @('afisat', 'trace', 'date') }
|
||||
@{ Prg = 'test_export_date.prg'; Steps = @('afisat', 'importat') }
|
||||
)
|
||||
if ($Doar) { $suita = $suita | Where-Object { $_.Prg -like "*$Doar*" } }
|
||||
|
||||
$rezultate = @()
|
||||
|
||||
if (-not $FaraSintaxa) {
|
||||
Write-Host '=== Verificare de sintaxa (compilare headless) ===' -ForegroundColor Cyan
|
||||
& (Join-Path $Teste 'verifica_sintaxa.ps1')
|
||||
$rezultate += [pscustomobject]@{ Test = 'verifica_sintaxa'; Rezultat = $(if ($LASTEXITCODE -eq 0) { 'PASS' } else { 'FAIL' }) }
|
||||
}
|
||||
|
||||
foreach ($t in $suita) {
|
||||
$prg = Join-Path $Teste $t.Prg
|
||||
if (-not (Test-Path $prg)) { Write-Warning "lipsa: $prg"; continue }
|
||||
Write-Host ''
|
||||
Write-Host ("=== {0} ===" -f $t.Prg) -ForegroundColor Cyan
|
||||
|
||||
# capturile fiecarui test in folderul lui, ca sa nu se suprascrie intre teste
|
||||
$shots = Join-Path (Join-Path $Teste 'screenshots') ([IO.Path]::GetFileNameWithoutExtension($t.Prg))
|
||||
& (Join-Path $Teste 'vfp_ui_harness.ps1') -TestPrg $prg -Steps $t.Steps -ShotsDir $shots -StepTimeoutSec 300
|
||||
|
||||
# verdictul il da testul: ultima linie "REZULTAT:" din log
|
||||
$log = Join-Path $Teste ([IO.Path]::GetFileNameWithoutExtension($t.Prg) + '_log.txt')
|
||||
$verdict = 'FARA LOG'
|
||||
if (Test-Path $log) {
|
||||
$linie = Select-String -Path $log -Pattern '^REZULTAT:' | Select-Object -Last 1
|
||||
if ($linie) { $verdict = $linie.Line -replace '^REZULTAT:\s*', '' }
|
||||
$failuri = @(Select-String -Path $log -Pattern '^FAIL:')
|
||||
foreach ($f in $failuri) { Write-Warning $f.Line }
|
||||
}
|
||||
$rezultate += [pscustomobject]@{ Test = $t.Prg; Rezultat = $verdict }
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host '=== SUMAR ===' -ForegroundColor Cyan
|
||||
$rezultate | Format-Table -AutoSize
|
||||
$nrFail = @($rezultate | Where-Object { $_.Rezultat -notlike 'PASS*' }).Count
|
||||
exit ([int]($nrFail -gt 0))
|
||||
63
Teste/test_conectare_roa.prg
Normal file
63
Teste/test_conectare_roa.prg
Normal file
@@ -0,0 +1,63 @@
|
||||
* test_conectare_roa.prg - verifica drumul de initializare CU conectare la Oracle.
|
||||
* Ruleaza cu datele din settings.ini; daca baza nu e accesibila, testul trebuie sa raporteze
|
||||
* esecul si sa iasa - NU sa ramana agatat pe un dialog de login (SQLSetProp DispLogin).
|
||||
*
|
||||
* vfp9.exe -A -T "Teste\test_conectare_roa.prg"
|
||||
* Nu face parte din suita implicita: depinde de reteaua/serverul Oracle.
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
|
||||
PUBLIC gcUILog, gcSyncDir, gnTestFail
|
||||
gnTestFail = 0
|
||||
|
||||
LOCAL lcTestDir, llConectat
|
||||
lcTestDir = ADDBS(JUSTPATH(SYS(16, 0)))
|
||||
gcUILog = lcTestDir + 'test_conectare_roa_log.txt'
|
||||
gcSyncDir = lcTestDir + 'uisync\'
|
||||
IF !DIRECTORY(gcSyncDir)
|
||||
MD (gcSyncDir)
|
||||
ENDIF
|
||||
STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
|
||||
|
||||
* fara dialog de login: headless, un prompt ODBC ar bloca procesul la nesfarsit
|
||||
SQLSETPROP(0, 'DispLogin', 3)
|
||||
|
||||
llConectat = DO_CONECTARE(lcTestDir)
|
||||
|
||||
HarnessLog('gnHandle = ' + TRANSFORM(gnHandle))
|
||||
TestAssert(TYPE('goConn') = 'O', 'goConn instantiat chiar si fara conectare')
|
||||
TestAssert(TYPE('gnHandle') = 'N', 'gnHandle este numeric')
|
||||
|
||||
IF llConectat
|
||||
TestAssert(gnHandle > 0, 'conectare reusita la ' + ALLTRIM(goApp.oSettings.host))
|
||||
LOCAL lnSucces
|
||||
lnSucces = goExecutor.oExecute(GetHash('cSql=>select 1 as unu from dual??cCursor=>cPing'))
|
||||
TestAssertEqual(1, lnSucces, 'interogare de test executata')
|
||||
IF USED('cPing')
|
||||
TestAssertEqual(1, cPing.unu, 'baza a raspuns corect')
|
||||
USE IN (SELECT('cPing'))
|
||||
ENDIF
|
||||
goConn.Disconnect()
|
||||
TestAssert(goConn.GetHandle() <= 0, 'deconectare reusita')
|
||||
ELSE
|
||||
HarnessLog('Conectare esuata (host ' + ALLTRIM(goApp.oSettings.host) + ;
|
||||
'). Testul a iesit controlat, fara sa ramana agatat.')
|
||||
TestAssert(gnHandle <= 0, 'esecul de conectare e raportat, nu ascuns')
|
||||
ENDIF
|
||||
|
||||
HarnessLog('REZULTAT: ' + TestRezultat())
|
||||
DO HarnessDone WITH TestRezultat()
|
||||
QUIT
|
||||
|
||||
FUNCTION DO_CONECTARE
|
||||
LPARAMETERS tcTestDir
|
||||
LOCAL llOk
|
||||
llOk = .F.
|
||||
DO (tcTestDir + 'test_init_env_auto.prg') WITH .T.
|
||||
SET PROCEDURE TO (tcTestDir + 'ui_harness.prg') ADDITIVE
|
||||
SET PROCEDURE TO (tcTestDir + 'mock_amessagebox.prg') ADDITIVE
|
||||
DO HarnessInit
|
||||
llOk = (TYPE('gnHandle') = 'N' AND gnHandle > 0)
|
||||
RETURN llOk
|
||||
ENDFUNC
|
||||
111
Teste/test_export_date.prg
Normal file
111
Teste/test_export_date.prg
Normal file
@@ -0,0 +1,111 @@
|
||||
* test_export_date.prg - test functional pe fluxul REAL de citire a fisierelor de import.
|
||||
* Instantiaza frm_import, seteaza intervalul din controale si apeleaza ExportDate() (pasul pe
|
||||
* care il declanseaza butonul "Import Fidelio&Biz > ROA"), apoi verifica cursoarele rezultate.
|
||||
* Nu are nevoie de Oracle: ExportDate citeste doar XML-urile din folderele din settings.ini.
|
||||
*
|
||||
* Parametri optionali: data de inceput si data de sfarsit ca AAAALLZZ.
|
||||
* vfp9.exe -A -T "Teste\test_export_date.prg" 20250601 20250630
|
||||
* Rulare cu capturi:
|
||||
* powershell -ExecutionPolicy Bypass -File Teste\vfp_ui_harness.ps1 `
|
||||
* -TestPrg 'D:\PROIECTE\FLORA\FLORA2ROA\Teste\test_export_date.prg' -Steps @('afisat','importat')
|
||||
|
||||
LPARAMETERS tcData1, tcData2
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
|
||||
PUBLIC gcUILog, gcSyncDir, gnTestFail, gnMockRaspuns, gcMockUltimMesaj, gnMockUltimTip
|
||||
gnTestFail = 0
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
gnMockUltimTip = 0
|
||||
|
||||
LOCAL lcTestDir, ldData1, ldData2
|
||||
lcTestDir = ADDBS(JUSTPATH(SYS(16, 0)))
|
||||
gcUILog = lcTestDir + 'test_export_date_log.txt'
|
||||
gcSyncDir = lcTestDir + 'uisync\'
|
||||
IF !DIRECTORY(gcSyncDir)
|
||||
MD (gcSyncDir)
|
||||
ENDIF
|
||||
STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
|
||||
|
||||
ldData1 = IIF(TYPE('tcData1') = 'C' AND LEN(ALLTRIM(TRANSFORM(tcData1))) = 8, ;
|
||||
DATE(VAL(LEFT(tcData1, 4)), VAL(SUBSTR(tcData1, 5, 2)), VAL(RIGHT(tcData1, 2))), DATE(2025, 6, 1))
|
||||
ldData2 = IIF(TYPE('tcData2') = 'C' AND LEN(ALLTRIM(TRANSFORM(tcData2))) = 8, ;
|
||||
DATE(VAL(LEFT(tcData2, 4)), VAL(SUBSTR(tcData2, 5, 2)), VAL(RIGHT(tcData2, 2))), DATE(2025, 6, 30))
|
||||
|
||||
DO (lcTestDir + 'test_init_env_auto.prg') WITH .F.
|
||||
|
||||
SET PROCEDURE TO (lcTestDir + 'ui_harness.prg') ADDITIVE
|
||||
SET PROCEDURE TO (lcTestDir + 'mock_amessagebox.prg') ADDITIVE
|
||||
|
||||
DO HarnessInit
|
||||
HarnessLog('Interval cerut: ' + DTOC(ldData1) + ' - ' + DTOC(ldData2))
|
||||
HarnessLog('cale_import_fidelio = ' + goApp.oSettings.cale_import_fidelio)
|
||||
HarnessLog('cale_import_biz = ' + goApp.oSettings.cale_import_biz)
|
||||
HarnessLog('cale_import = ' + goApp.oSettings.cale_import)
|
||||
|
||||
TestAssert(DIRECTORY(ADDBS(ALLTRIM(goApp.oSettings.cale_import_fidelio))), 'exista directorul Fidelio')
|
||||
TestAssert(DIRECTORY(ADDBS(ALLTRIM(goApp.oSettings.cale_import_biz))), 'exista directorul Biz')
|
||||
TestAssert(DIRECTORY(ADDBS(ALLTRIM(goApp.oSettings.cale_import))), 'exista directorul de import')
|
||||
|
||||
PUBLIC goImportFrm
|
||||
DO FORM (gcAppPath + 'frm_import') NAME goImportFrm NOSHOW
|
||||
goImportFrm.WindowType = 0
|
||||
goImportFrm.Show()
|
||||
DOEVENTS FORCE
|
||||
|
||||
DO HarnessStep WITH 0, 'formular afisat'
|
||||
|
||||
* fluxul real: utilizatorul completeaza intervalul in controale, apoi apasa butonul de import
|
||||
goImportFrm.txtData1.Value = ldData1
|
||||
goImportFrm.txtData2.Value = ldData2
|
||||
goImportFrm.chkTest.Value = 1
|
||||
LOCAL lnSucces
|
||||
lnSucces = goImportFrm.ExportDate()
|
||||
DOEVENTS FORCE
|
||||
|
||||
HarnessLog('ExportDate a returnat ' + TRANSFORM(lnSucces))
|
||||
TestAssertEqual(1, lnSucces, 'ExportDate returneaza CT_SUCCES')
|
||||
|
||||
LOCAL lnFidelio, lnIntrari, lnIesiri, lnVanzari, lnPart
|
||||
lnFidelio = IIF(USED('cFacturiFidelio'), RECCOUNT('cFacturiFidelio'), -1)
|
||||
lnIntrari = IIF(USED('cIntrariRestaurant'), RECCOUNT('cIntrariRestaurant'), -1)
|
||||
lnIesiri = IIF(USED('cIesiriRestaurant'), RECCOUNT('cIesiriRestaurant'), -1)
|
||||
lnVanzari = IIF(USED('cVanzariRestaurant'), RECCOUNT('cVanzariRestaurant'), -1)
|
||||
lnPart = IIF(USED('nPart'), RECCOUNT('nPart'), -1)
|
||||
HarnessLog('Randuri citite: facturi Fidelio=' + TRANSFORM(lnFidelio) + ' intrari=' + TRANSFORM(lnIntrari) + ;
|
||||
' iesiri=' + TRANSFORM(lnIesiri) + ' vanzari=' + TRANSFORM(lnVanzari) + ' parteneri=' + TRANSFORM(lnPart))
|
||||
|
||||
TestAssert(lnFidelio >= 0, 'cursorul cFacturiFidelio exista')
|
||||
TestAssert(lnIntrari >= 0, 'cursorul cIntrariRestaurant exista')
|
||||
TestAssert(lnIesiri >= 0, 'cursorul cIesiriRestaurant exista')
|
||||
TestAssert(lnVanzari >= 0, 'cursorul cVanzariRestaurant exista')
|
||||
TestAssert(lnPart >= 0, 'cursorul nPart exista')
|
||||
TestAssert(lnFidelio + lnIntrari + lnIesiri + lnVanzari > 0, ;
|
||||
's-au citit randuri din fisierele intervalului')
|
||||
|
||||
* datele citite trebuie sa fie DOAR din intervalul cerut (filtrul pe numele fisierului)
|
||||
IF lnVanzari > 0
|
||||
LOCAL lnInAfara
|
||||
SELECT cVanzariRestaurant
|
||||
COUNT FOR !BETWEEN(CTOD(LEFT(ALLTRIM(NVL(datadocument, '')), 10)), ldData1, ldData2) ;
|
||||
AND !EMPTY(ALLTRIM(NVL(datadocument, ''))) TO lnInAfara
|
||||
TestAssertEqual(0, lnInAfara, 'vanzarile citite sunt in intervalul cerut')
|
||||
ENDIF
|
||||
|
||||
* cheia externa de partener se calculeaza pentru toate randurile
|
||||
IF lnPart > 0
|
||||
LOCAL lnFaraCod
|
||||
SELECT nPart
|
||||
COUNT FOR EMPTY(NVL(cod, '')) TO lnFaraCod
|
||||
TestAssertEqual(0, lnFaraCod, 'toti partenerii au cod cheie externa calculat')
|
||||
ENDIF
|
||||
|
||||
DO HarnessStep WITH 1, 'import citit: ' + TRANSFORM(lnFidelio + lnIntrari + lnIesiri + lnVanzari) + ' randuri'
|
||||
|
||||
HarnessLog('REZULTAT: ' + TestRezultat())
|
||||
DO HarnessDone WITH TestRezultat()
|
||||
|
||||
goImportFrm.Release()
|
||||
QUIT
|
||||
82
Teste/test_frm_import_ui.prg
Normal file
82
Teste/test_frm_import_ui.prg
Normal file
@@ -0,0 +1,82 @@
|
||||
* test_frm_import_ui.prg - test UI smoke pentru formularul real frm_import.
|
||||
* Verifica pornirea aplicatiei fara Oracle: mediul se initializeaza, formularul se instantiaza,
|
||||
* Init completeaza intervalul de date din settings.ini, iar metoda Trace scrie in edtInfo.
|
||||
*
|
||||
* Rulare (orchestrator + capturi):
|
||||
* powershell -ExecutionPolicy Bypass -File Teste\vfp_ui_harness.ps1 `
|
||||
* -TestPrg 'D:\PROIECTE\FLORA\FLORA2ROA\Teste\test_frm_import_ui.prg' `
|
||||
* -Steps @('afisat','trace','date')
|
||||
* Rulare fara capturi: vfp9.exe -A -T "Teste\test_frm_import_ui.prg" (auto-continue la 30s/pas)
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
|
||||
PUBLIC gcUILog, gcSyncDir, gnTestFail, gnMockRaspuns, gcMockUltimMesaj, gnMockUltimTip
|
||||
gnTestFail = 0
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
gnMockUltimTip = 0
|
||||
|
||||
LOCAL lcTestDir
|
||||
lcTestDir = ADDBS(JUSTPATH(SYS(16, 0)))
|
||||
gcUILog = lcTestDir + 'test_frm_import_ui_log.txt'
|
||||
gcSyncDir = lcTestDir + 'uisync\'
|
||||
IF !DIRECTORY(gcSyncDir)
|
||||
MD (gcSyncDir)
|
||||
ENDIF
|
||||
STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
|
||||
|
||||
DO (lcTestDir + 'test_init_env_auto.prg') WITH .F.
|
||||
|
||||
SET PROCEDURE TO (lcTestDir + 'ui_harness.prg') ADDITIVE
|
||||
* mock-ul de dialoguri se incarca ULTIMUL, ca sa umbreasca amessagebox din myutils.prg
|
||||
SET PROCEDURE TO (lcTestDir + 'mock_amessagebox.prg') ADDITIVE
|
||||
|
||||
DO HarnessInit
|
||||
HarnessLog('Mediu initializat. goApp=' + TYPE('goApp') + ' goExecutor=' + TYPE('goExecutor') + ;
|
||||
' goConn=' + TYPE('goConn'))
|
||||
|
||||
TestAssert(TYPE('goApp') = 'O', 'goApp instantiat')
|
||||
TestAssert(TYPE('goExecutor') = 'O', 'goExecutor instantiat')
|
||||
TestAssert(TYPE('goConn') = 'O', 'goConn instantiat')
|
||||
TestAssert(TYPE('goApp.oSettings') = 'O', 'settings.ini citit in goApp.oSettings')
|
||||
|
||||
PUBLIC goImportFrm
|
||||
DO FORM (gcAppPath + 'frm_import') NAME goImportFrm NOSHOW
|
||||
* modeless: Show(1) pe un formular modal nu returneaza si blocheaza testul headless
|
||||
goImportFrm.WindowType = 0
|
||||
goImportFrm.Show()
|
||||
goImportFrm.WindowState = 2
|
||||
DOEVENTS FORCE
|
||||
|
||||
TestAssert(TYPE('goImportFrm') = 'O', 'frm_import instantiat')
|
||||
TestAssert(TYPE('goImportFrm.txtData1.Value') = 'D', 'txtData1 are valoare data')
|
||||
TestAssert(TYPE('goImportFrm.txtData2.Value') = 'D', 'txtData2 are valoare data')
|
||||
TestAssert(goImportFrm.txtData1.Value <= goImportFrm.txtData2.Value, ;
|
||||
'interval valid: ' + DTOC(goImportFrm.txtData1.Value) + ' - ' + DTOC(goImportFrm.txtData2.Value))
|
||||
TestAssertEqual(1, goImportFrm.chkTest.Value, 'bifa Test activa implicit (nu scrie note in ROA)')
|
||||
|
||||
DO HarnessStep WITH 0, 'formular afisat'
|
||||
|
||||
* Trace este canalul prin care formularul raporteaza progresul importului
|
||||
goImportFrm.Trace('Verificare harness: mesaj de test')
|
||||
DOEVENTS FORCE
|
||||
TestAssert('Verificare harness' $ goImportFrm.edtInfo.Value, 'Trace scrie in edtInfo')
|
||||
|
||||
DO HarnessStep WITH 1, 'trace scris in edtInfo'
|
||||
|
||||
* schimbarea intervalului se reflecta in controale (fluxul real al utilizatorului)
|
||||
goImportFrm.txtData1.Value = DATE(2025, 6, 1)
|
||||
goImportFrm.txtData2.Value = DATE(2025, 6, 30)
|
||||
goImportFrm.Refresh()
|
||||
DOEVENTS FORCE
|
||||
TestAssertEqual(DATE(2025, 6, 1), goImportFrm.txtData1.Value, 'data inceput modificata')
|
||||
TestAssertEqual(DATE(2025, 6, 30), goImportFrm.txtData2.Value, 'data sfarsit modificata')
|
||||
|
||||
DO HarnessStep WITH 2, 'interval 06/2025 setat'
|
||||
|
||||
HarnessLog('REZULTAT: ' + TestRezultat())
|
||||
DO HarnessDone WITH TestRezultat()
|
||||
|
||||
goImportFrm.Release()
|
||||
QUIT
|
||||
55
Teste/test_helpers.prg
Normal file
55
Teste/test_helpers.prg
Normal file
@@ -0,0 +1,55 @@
|
||||
* test_helpers.prg - proceduri comune testelor FLORA2ROA (handler de erori headless + aserturi).
|
||||
* Se incarca automat din test_init_env_auto.prg cu SET PROCEDURE ... ADDITIVE.
|
||||
* TestErrorHandler - ON ERROR pentru rulare fara consola: logheaza si continua
|
||||
* TestAssert(cond, mesaj) - PASS/FAIL in log; incrementeaza gnTestFail la esec
|
||||
* TestAssertEqual(asteptat, obtinut, mesaj)
|
||||
* TestRezultat() - 'PASS'/'FAIL' + numarul de aserturi picate
|
||||
* TestLog(mesaj) - scrie in gcUILog (harness) sau in gcTestErrLog
|
||||
|
||||
PROCEDURE TestErrorHandler
|
||||
LPARAMETERS tnError, tcMessage, tcProgram, tnLine
|
||||
LOCAL lcLinie
|
||||
lcLinie = TTOC(DATETIME()) + ' EROARE ' + TRANSFORM(tnError) + ': ' + TRANSFORM(tcMessage) + ;
|
||||
' | program: ' + TRANSFORM(tcProgram) + ' | linia: ' + TRANSFORM(tnLine)
|
||||
IF TYPE('gcTestErrLog') = 'C' AND !EMPTY(gcTestErrLog)
|
||||
SET SAFETY OFF
|
||||
STRTOFILE(lcLinie + CHR(13) + CHR(10), gcTestErrLog, .T.)
|
||||
ENDIF
|
||||
TestLog(lcLinie)
|
||||
ENDPROC
|
||||
|
||||
PROCEDURE TestLog
|
||||
LPARAMETERS tcMesaj
|
||||
IF TYPE('gcUILog') = 'C' AND !EMPTY(gcUILog)
|
||||
DO HarnessLog WITH TRANSFORM(tcMesaj)
|
||||
ENDIF
|
||||
ENDPROC
|
||||
|
||||
FUNCTION TestAssert
|
||||
LPARAMETERS tlConditie, tcMesaj
|
||||
IF TYPE('gnTestFail') <> 'N'
|
||||
PUBLIC gnTestFail
|
||||
gnTestFail = 0
|
||||
ENDIF
|
||||
IF tlConditie
|
||||
TestLog('PASS: ' + TRANSFORM(tcMesaj))
|
||||
ELSE
|
||||
gnTestFail = gnTestFail + 1
|
||||
TestLog('FAIL: ' + TRANSFORM(tcMesaj))
|
||||
ENDIF
|
||||
RETURN tlConditie
|
||||
ENDFUNC
|
||||
|
||||
FUNCTION TestAssertEqual
|
||||
LPARAMETERS tuAsteptat, tuObtinut, tcMesaj
|
||||
LOCAL llEgal
|
||||
llEgal = (TRANSFORM(tuAsteptat) == TRANSFORM(tuObtinut))
|
||||
RETURN TestAssert(llEgal, TRANSFORM(tcMesaj) + ' [asteptat: ' + TRANSFORM(tuAsteptat) + ;
|
||||
' | obtinut: ' + TRANSFORM(tuObtinut) + ']')
|
||||
ENDFUNC
|
||||
|
||||
FUNCTION TestRezultat
|
||||
LOCAL lnFail
|
||||
lnFail = IIF(TYPE('gnTestFail') = 'N', gnTestFail, 0)
|
||||
RETURN IIF(lnFail = 0, 'PASS', 'FAIL (' + TRANSFORM(lnFail) + ' aserturi picate)')
|
||||
ENDFUNC
|
||||
102
Teste/test_init_env_auto.prg
Normal file
102
Teste/test_init_env_auto.prg
Normal file
@@ -0,0 +1,102 @@
|
||||
* test_init_env_auto.prg
|
||||
* Initializare NEINTERACTIVA a mediului FLORA2ROA pentru rulare headless
|
||||
* (vfp9.exe -A -T "script.prg"), echivalentul programului principal main.prg fara
|
||||
* DO FORM / READ EVENTS.
|
||||
*
|
||||
* Utilizare dintr-un script de test:
|
||||
* DO Teste\test_init_env_auto WITH .F. && fara conectare Oracle (implicit)
|
||||
* DO Teste\test_init_env_auto WITH .T. && conectare cu datele din settings.ini
|
||||
* DO Teste\test_init_env_auto WITH .T., 'ROA', 'XENOTI', 'parola'
|
||||
*
|
||||
* Parametri:
|
||||
* tlConnect - .T. = se conecteaza la Oracle (goConn.Connect); implicit .F.
|
||||
* tcHost/tcUser/tcPassword - suprascriu datele din settings.ini
|
||||
* tcAppPath - radacina aplicatiei; implicit se deduce din calea acestui program
|
||||
*
|
||||
* Dupa apel sunt disponibile: gcAppName, gcAppPath, gcTempPath, goLog, goApp, goConn,
|
||||
* goExecutor, gnHandle si SET PROCEDURE / SET LIBRARY ca in main.prg. Se poate face
|
||||
* CREATEOBJECT('frm_import') sau DO FORM frm_import si apela metodele punctual.
|
||||
*
|
||||
* ATENTIE: ErrorHandler-ul din main.prg deschide MESSAGEBOX (blocheaza headless). Aici se
|
||||
* instaleaza TestErrorHandler (test_helpers.prg), care logheaza in gcTestErrLog si continua.
|
||||
|
||||
LPARAMETERS tlConnect, tcHost, tcUser, tcPassword, tcAppPath
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
CLOSE DATABASES ALL
|
||||
SET DELETED ON
|
||||
SET EXCLUSIVE ON
|
||||
SET ANSI ON
|
||||
SET EXACT ON
|
||||
SET CENTURY ON
|
||||
SET DATE DMY
|
||||
SET NULLDISPLAY TO ''
|
||||
|
||||
PUBLIC gcAppName, gcAppPath, gcTempPath, gcTestErrLog
|
||||
PUBLIC goApp, goLog, goConn, goExecutor, goImportFrm, gnHandle
|
||||
|
||||
LOCAL lcHere
|
||||
lcHere = ADDBS(JUSTPATH(SYS(16, 0)))
|
||||
IF TYPE('tcAppPath') = 'C' AND !EMPTY(tcAppPath)
|
||||
gcAppPath = ADDBS(tcAppPath)
|
||||
ELSE
|
||||
* scriptul sta in <radacina>\Teste\, deci radacina e cu un nivel mai sus
|
||||
gcAppPath = ADDBS(JUSTPATH(SUBSTR(lcHere, 1, LEN(lcHere) - 1)))
|
||||
ENDIF
|
||||
|
||||
gcAppName = 'FLORA2ROA'
|
||||
gcTempPath = GETENV("TEMP")
|
||||
gcTestErrLog = gcAppPath + 'Teste\_erori_test.txt'
|
||||
SET DEFAULT TO (gcAppPath)
|
||||
SET PATH TO (gcAppPath) ADDITIVE
|
||||
SET PATH TO (gcAppPath + 'Teste') ADDITIVE
|
||||
|
||||
* aceleasi fisiere de proceduri ca in main.prg; mock-urile se incarca DUPA, in test
|
||||
SET PROCEDURE TO (gcAppPath + 'capp.prg') ADDITIVE
|
||||
SET PROCEDURE TO (gcAppPath + 'app.prg') ADDITIVE
|
||||
SET PROCEDURE TO (gcAppPath + 'myutils.prg') ADDITIVE
|
||||
SET PROCEDURE TO (gcAppPath + 'vfpconnection_utils.prg') ADDITIVE
|
||||
SET PROCEDURE TO (gcAppPath + 'myapp.prg') ADDITIVE
|
||||
SET PROCEDURE TO (gcAppPath + 'Teste\test_helpers.prg') ADDITIVE
|
||||
|
||||
LOCAL lnLib, lcLib
|
||||
FOR lnLib = 1 TO 2
|
||||
lcLib = IIF(lnLib = 1, 'vfpconnection.fll', 'vfpencryption.fll')
|
||||
IF FILE(gcAppPath + lcLib)
|
||||
TRY
|
||||
SET LIBRARY TO (gcAppPath + lcLib) ADDITIVE
|
||||
CATCH
|
||||
STRTOFILE('Nu s-a putut incarca ' + lcLib + CHR(13) + CHR(10), gcTestErrLog, .T.)
|
||||
ENDTRY
|
||||
ENDIF
|
||||
ENDFOR
|
||||
|
||||
ON ERROR DO TestErrorHandler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
|
||||
|
||||
goImportFrm = NULL
|
||||
goConn = CREATEOBJECT("oConn")
|
||||
goExecutor = CREATEOBJECT("oExecutor")
|
||||
goLog = CREATEOBJECT("Log", gcAppPath + "log\log_test_" + gcAppName + "_" + DTOS(DATE()) + ".txt")
|
||||
goApp = CREATEOBJECT("MyApp")
|
||||
gnHandle = 0
|
||||
|
||||
IF TYPE('tcHost') = 'C' AND !EMPTY(tcHost)
|
||||
goApp.oSettings.host = ALLTRIM(tcHost)
|
||||
ENDIF
|
||||
IF TYPE('tcUser') = 'C' AND !EMPTY(tcUser)
|
||||
goApp.oSettings.user = ALLTRIM(tcUser)
|
||||
ENDIF
|
||||
IF TYPE('tcPassword') = 'C' AND !EMPTY(tcPassword)
|
||||
goApp.oSettings.password = ALLTRIM(tcPassword)
|
||||
ENDIF
|
||||
|
||||
IF TYPE('tlConnect') = 'L' AND tlConnect
|
||||
goConn.Connect()
|
||||
gnHandle = goConn.GetHandle()
|
||||
IF gnHandle <= 0
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
RETURN .T.
|
||||
35
Teste/text_din_bin.ps1
Normal file
35
Teste/text_din_bin.ps1
Normal file
@@ -0,0 +1,35 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Genereaza/improspateaza versiunile TEXT (.sc2/.vc2/.pj2) ale binarelor VFP din FLORA2ROA.
|
||||
|
||||
.DESCRIPTION
|
||||
Wrapper peste D:\ROA\UTIL\foxbin2prg\git_sync.ps1 cu parametrii acestui proiect (flux
|
||||
in-arbore: textul sta LANGA binar, nu intr-un cache extern). Git urmareste textele .??2;
|
||||
binarele VFP sunt git-ignored, iar SVN ramane sursa de adevar pentru ele.
|
||||
|
||||
Se ruleaza la inceput de sesiune si INAINTE de orice git commit, ca textele sa reflecte
|
||||
binarele curente. Conversia e incrementala si nu atinge niciodata binarele.
|
||||
|
||||
Fluxul complet si capcanele: COMUN\docs\flux-editare-vfp-text.md,
|
||||
COMUN\docs\inrolare-proiect-git-text.md.
|
||||
|
||||
.PARAMETER Force
|
||||
Reconverteste chiar daca textul e la zi.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\text_din_bin.ps1
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ProjectRoot,
|
||||
[switch]$Force
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent }
|
||||
$tool = 'D:\ROA\UTIL\foxbin2prg\git_sync.ps1'
|
||||
if (-not (Test-Path $tool)) { throw "Nu gasesc $tool (tooling-ul partajat FoxBin2Prg)." }
|
||||
|
||||
# -DbfList gol: proiectul nu are tabele de configurare de urmarit ca text
|
||||
& $tool -ProjectRoot $ProjectRoot -DbfList @() -Force:$Force
|
||||
exit $LASTEXITCODE
|
||||
64
Teste/ui_harness.prg
Normal file
64
Teste/ui_harness.prg
Normal file
@@ -0,0 +1,64 @@
|
||||
* ui_harness.prg - proceduri reutilizabile pentru teste UI VFP cu handshake pe semafoare.
|
||||
* Un test seteaza PUBLIC gcSyncDir (folder semafoare, cu \ final) si gcUILog (fisier log),
|
||||
* apoi SET PROCEDURE TO ui_harness ADDITIVE si foloseste:
|
||||
* HarnessLog(mesaj) - scrie o linie in log
|
||||
* HarnessStep(n, mesaj [, autoSec]) - semnaleaza pasul n gata + asteapta continuarea
|
||||
* HarnessReady(n, mesaj) / HarnessWaitContinue(n [, autoSec]) - variantele separate
|
||||
* HarnessDone([status]) - scrie done.txt
|
||||
* HarnessWaitContinue asteapta cont_<n>.txt; daca nu apare in autoSec secunde (implicit 30),
|
||||
* continua singur (auto-continue) => testul merge si FARA orchestrator (doar log, fara capturi).
|
||||
|
||||
PROCEDURE HarnessInit
|
||||
*-- defensiv: SAFETY OFF ca STRTOFILE peste un fisier existent sa nu scoata dialogul modal
|
||||
*-- "overwrite?" (blocheaza headless); TALK OFF ca SUM/CALCULATE sa nu deseneze pe desktop.
|
||||
*-- Un test ar trebui sa faca oricum SET SAFETY OFF INAINTE de prima lui scriere de log.
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
ENDPROC
|
||||
|
||||
PROCEDURE HarnessLog
|
||||
LPARAMETERS tcMsg
|
||||
IF TYPE('gcUILog') <> 'C' OR EMPTY(gcUILog)
|
||||
RETURN
|
||||
ENDIF
|
||||
SET SAFETY OFF
|
||||
LOCAL lcL
|
||||
lcL = IIF(FILE(gcUILog), FILETOSTR(gcUILog), '')
|
||||
STRTOFILE(lcL + tcMsg + CHR(13)+CHR(10), gcUILog)
|
||||
ENDPROC
|
||||
|
||||
PROCEDURE HarnessReady
|
||||
LPARAMETERS tnStep, tcNote
|
||||
SET SAFETY OFF
|
||||
STRTOFILE(TTOC(DATETIME()) + ' ' + tcNote + CHR(13)+CHR(10), gcSyncDir + 'ready_' + TRANSFORM(tnStep) + '.txt')
|
||||
HarnessLog('READY ' + TRANSFORM(tnStep) + ' - ' + tcNote)
|
||||
ENDPROC
|
||||
|
||||
FUNCTION HarnessWaitContinue
|
||||
LPARAMETERS tnStep, tnAutoSec
|
||||
LOCAL lcCont, lnMax, lnI, llPrimit
|
||||
lcCont = gcSyncDir + 'cont_' + TRANSFORM(tnStep) + '.txt'
|
||||
lnMax = IIF(TYPE('tnAutoSec') = 'N' AND tnAutoSec > 0, tnAutoSec, 30)
|
||||
lnI = 0
|
||||
DO WHILE !FILE(lcCont) AND lnI < (lnMax * 5)
|
||||
lnI = lnI + 1
|
||||
DOEVENTS FORCE
|
||||
= INKEY(0.2)
|
||||
ENDDO
|
||||
llPrimit = FILE(lcCont)
|
||||
HarnessLog('CONTINUE ' + TRANSFORM(tnStep) + IIF(llPrimit, ' (semnal primit)', ' (auto-continue dupa ' + TRANSFORM(lnMax) + 's)'))
|
||||
RETURN llPrimit
|
||||
ENDFUNC
|
||||
|
||||
PROCEDURE HarnessStep
|
||||
LPARAMETERS tnStep, tcNote, tnAutoSec
|
||||
HarnessReady(tnStep, tcNote)
|
||||
HarnessWaitContinue(tnStep, tnAutoSec)
|
||||
ENDPROC
|
||||
|
||||
PROCEDURE HarnessDone
|
||||
LPARAMETERS tcStatus
|
||||
SET SAFETY OFF
|
||||
STRTOFILE(IIF(EMPTY(tcStatus), 'done', tcStatus) + ' ' + TTOC(DATETIME()) + CHR(13)+CHR(10), gcSyncDir + 'done.txt')
|
||||
HarnessLog('GATA')
|
||||
ENDPROC
|
||||
128
Teste/verifica_sintaxa.ps1
Normal file
128
Teste/verifica_sintaxa.ps1
Normal file
@@ -0,0 +1,128 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verificare de sintaxa prin compilare headless a fisierelor .prg din FLORA2ROA.
|
||||
|
||||
.DESCRIPTION
|
||||
Cel mai ieftin test dupa orice editare de .prg (vezi COMUN\docs\depanare_testare_vfp.md, sect. 4).
|
||||
Compileaza COPII ale fisierelor intr-un folder temporar, ca .fxp-urile si .err-urile generate
|
||||
sa nu murdareasca arborele proiectului. VFP nu rezolva simboluri externe la compilare, deci
|
||||
copia izolata e suficienta pentru sintaxa.
|
||||
|
||||
Toate fisierele se compileaza intr-o SINGURA rulare vfp9: lansarile repetate de vfp9 din
|
||||
aceeasi sesiune PowerShell esueaza tacut (procesul porneste dar nu executa - vezi
|
||||
COMUN\docs\testare-ui-vfp.md, capcana d).
|
||||
|
||||
Verdict per fisier: existenta unui <nume>.err dupa compilare. Se raporteaza si liniile mai
|
||||
lungi de 250 de caractere (capcana VFP: literal/linie prea lunga = eroare de compilare sau
|
||||
eroare 11 derutanta la runtime).
|
||||
|
||||
NU inlocuieste testul functional: prinde sintaxa, nu apeluri catre metode inexistente.
|
||||
|
||||
.PARAMETER Files
|
||||
Fisiere .prg de verificat. Implicit toate .prg-urile din radacina proiectului si din Teste\.
|
||||
|
||||
.PARAMETER ProjectRoot
|
||||
Radacina proiectului (implicit folderul parinte al acestui script).
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\verifica_sintaxa.ps1
|
||||
|
||||
.EXAMPLE
|
||||
powershell -ExecutionPolicy Bypass -File Teste\verifica_sintaxa.ps1 -Files myutils.prg,app.prg
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string[]]$Files,
|
||||
[string]$ProjectRoot,
|
||||
[int]$MaxLineLen = 250,
|
||||
[string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent }
|
||||
if (-not (Test-Path $Vfp)) { throw "Nu gasesc vfp9.exe: $Vfp" }
|
||||
|
||||
if (-not $Files) {
|
||||
$Files = @(Get-ChildItem -Path $ProjectRoot -Filter '*.prg' -File | ForEach-Object { $_.FullName })
|
||||
$Files += @(Get-ChildItem -Path (Join-Path $ProjectRoot 'Teste') -Filter '*.prg' -File -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName })
|
||||
}
|
||||
$Files = $Files | ForEach-Object { if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $ProjectRoot $_ } }
|
||||
|
||||
$work = Join-Path $env:TEMP ('flora2roa_syntax_' + [Guid]::NewGuid().ToString('N').Substring(0,8))
|
||||
New-Item -ItemType Directory -Force -Path $work | Out-Null
|
||||
|
||||
# fisierele .h referite prin #INCLUDE trebuie sa fie langa copie, altfel compilarea da eroare
|
||||
Get-ChildItem -Path $ProjectRoot -Filter '*.h' -File -ErrorAction SilentlyContinue |
|
||||
ForEach-Object { Copy-Item $_.FullName (Join-Path $work $_.Name) -Force }
|
||||
|
||||
# omoara doar instantele vfp9 proprii (linia de comanda contine folderul de lucru temporar)
|
||||
function Stop-VfpProprii([string]$Marker) {
|
||||
Get-CimInstance Win32_Process -Filter "Name='vfp9.exe'" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
if ($_.CommandLine -and $_.CommandLine.ToLower().Contains($Marker.ToLower())) {
|
||||
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# copiaza sursele + strange liniile prea lungi (analiza statica, fara VFP)
|
||||
$copii = @()
|
||||
$liniiLungi = @{}
|
||||
foreach ($f in $Files) {
|
||||
if (-not (Test-Path $f)) { Write-Warning "lipsa: $f"; continue }
|
||||
$nume = [IO.Path]::GetFileName($f)
|
||||
$copie = Join-Path $work $nume
|
||||
Copy-Item -LiteralPath $f -Destination $copie -Force
|
||||
$copii += [pscustomobject]@{ Nume = $nume; Sursa = $f; Copie = $copie }
|
||||
|
||||
$lungi = @()
|
||||
$linii = [IO.File]::ReadAllLines($f, [Text.Encoding]::GetEncoding(1252))
|
||||
for ($i = 0; $i -lt $linii.Length; $i++) {
|
||||
if ($linii[$i].Length -gt $MaxLineLen) { $lungi += ('{0} (len={1})' -f ($i + 1), $linii[$i].Length) }
|
||||
}
|
||||
if ($lungi) { $liniiLungi[$nume] = ($lungi -join '; ') }
|
||||
}
|
||||
if (-not $copii) { Write-Host 'Nimic de verificat.'; return }
|
||||
|
||||
# un singur .prg care compileaza toate copiile (SAFETY OFF: fara dialog "overwrite?" headless)
|
||||
$linii = @('SET SAFETY OFF', 'SET TALK OFF')
|
||||
foreach ($c in $copii) { $linii += ('lcT = "{0}"' -f $c.Copie); $linii += 'COMPILE (lcT)' }
|
||||
$linii += 'STRTOFILE("gata", "' + (Join-Path $work '_gata.txt') + '")'
|
||||
$linii += 'QUIT'
|
||||
$tmpPrg = Join-Path $work '_compile_all.prg'
|
||||
Set-Content -Path $tmpPrg -Value (($linii -join "`r`n") + "`r`n") -Encoding ascii
|
||||
|
||||
# -A ignora config.fpw implicit, -T sare peste splash; ambele obligatorii headless
|
||||
$p = Start-Process -FilePath $Vfp -ArgumentList @('-A', '-T', "`"$tmpPrg`"") -PassThru -WindowStyle Minimized
|
||||
if (-not $p.WaitForExit(180000)) { Write-Warning 'Timeout la compilare (180s).'; try { $p.Kill() } catch {} }
|
||||
Stop-VfpProprii $work
|
||||
if (-not (Test-Path (Join-Path $work '_gata.txt'))) {
|
||||
Write-Warning 'Compilarea nu a ajuns la final - verdictul per fisier poate fi incomplet.'
|
||||
}
|
||||
|
||||
$rezultate = foreach ($c in $copii) {
|
||||
$err = [IO.Path]::ChangeExtension($c.Copie, 'err')
|
||||
$fxp = [IO.Path]::ChangeExtension($c.Copie, 'fxp')
|
||||
$erori = if (Test-Path $err) { (Get-Content -Raw $err).Trim() } else { '' }
|
||||
[pscustomobject]@{
|
||||
Fisier = $c.Nume
|
||||
Sintaxa = if ($erori) { 'ERORI' } elseif (Test-Path $fxp) { 'OK' } else { 'NECOMPILAT' }
|
||||
Erori = $erori
|
||||
LiniiLungi = $(if ($liniiLungi.ContainsKey($c.Nume)) { $liniiLungi[$c.Nume] } else { '' })
|
||||
}
|
||||
}
|
||||
|
||||
Remove-Item -Recurse -Force $work -ErrorAction SilentlyContinue
|
||||
|
||||
$rezultate | Format-Table Fisier, Sintaxa -AutoSize
|
||||
foreach ($r in $rezultate | Where-Object { $_.Erori }) {
|
||||
Write-Host ''
|
||||
Write-Warning ("ERORI in {0}:`n{1}" -f $r.Fisier, $r.Erori)
|
||||
}
|
||||
foreach ($r in $rezultate | Where-Object { $_.LiniiLungi }) {
|
||||
Write-Host ''
|
||||
Write-Warning ("Linii peste $MaxLineLen caractere in {0}: {1}" -f $r.Fisier, $r.LiniiLungi)
|
||||
}
|
||||
$nrProbleme = @($rezultate | Where-Object { $_.Sintaxa -ne 'OK' }).Count
|
||||
Write-Host ''
|
||||
Write-Host ("Gata. Fisiere verificate: {0} Cu probleme: {1}" -f @($rezultate).Count, $nrProbleme)
|
||||
exit ([int]($nrProbleme -gt 0))
|
||||
231
Teste/vfp_ui_harness.ps1
Normal file
231
Teste/vfp_ui_harness.ps1
Normal file
@@ -0,0 +1,231 @@
|
||||
# vfp_ui_harness.ps1 - harness reutilizabil pentru teste UI VFP cu capturi de ecran.
|
||||
# Lanseaza un test VFP care afiseaza un formular real, il conduce pas cu pas prin semafoare
|
||||
# (ready_<n>.txt scris de test <-> cont_<n>.txt scris de harness), face un screenshot dupa
|
||||
# fiecare pas si il valideaza. Testul VFP foloseste procedurile din ui_harness.prg.
|
||||
#
|
||||
# Exemplu:
|
||||
# powershell -ExecutionPolicy Bypass -File vfp_ui_harness.ps1 `
|
||||
# -TestPrg 'D:\...\test_x_ui.prg' -Steps @('afisat','actiune1','actiune2')
|
||||
#
|
||||
# Capcane rezolvate (vezi docs\testare-ui-vfp.md): precompilare izolata, lansare .fxp,
|
||||
# detectie pornire pe mtime log, timeout-uri generoase, kill zombi vfp9.
|
||||
#
|
||||
# FARA FURT DE FOCUS (cerinta Marius, 17/07/2026): capturile NU mai folosesc
|
||||
# Graphics.CopyFromScreen (poza ecranului intreg - fura focus / se corupe daca utilizatorul
|
||||
# lucreaza in paralel). Se foloseste PrintWindow pe handle-ul ferestrei vfp9 + fereastra e
|
||||
# mutata OFF-SCREEN (x=-4000, HWND_BOTTOM, SWP_NOACTIVATE) imediat ce apare handle-ul si
|
||||
# re-impinsa inainte de fiecare pas (VFP isi poate re-activa fereastra la Show()/dialoguri).
|
||||
# Fereastra ramane "visible" pentru GDI (PrintWindow merge), doar nu e pe ecranul vizibil
|
||||
# utilizatorului si nu ia focus.
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$TestPrg,
|
||||
[Parameter(Mandatory=$true)][string[]]$Steps, # etichete pentru pasii 0..N-1
|
||||
[int]$StepTimeoutSec = 130, # cat astept fiecare ready_<n> (dupa pas 0)
|
||||
[int]$ReadyTimeoutSec = 180, # cat astept ready_0 (afisarea formularului, incarcare lenta)
|
||||
[string]$ShotsDir,
|
||||
[string]$SyncDir,
|
||||
[string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$TestDir = [System.IO.Path]::GetDirectoryName($TestPrg)
|
||||
$TestFxp = [System.IO.Path]::ChangeExtension($TestPrg, 'fxp')
|
||||
$TestLog = [System.IO.Path]::ChangeExtension($TestPrg, $null) + '_log.txt'
|
||||
if (-not $SyncDir) { $SyncDir = Join-Path $TestDir 'uisync' }
|
||||
if (-not $ShotsDir) { $ShotsDir = Join-Path $TestDir 'screenshots' }
|
||||
$Precompile = Join-Path $PSScriptRoot '_precompile.ps1'
|
||||
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class Win32Ui {
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT { public int Left, Top, Right, Bottom; }
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdc, uint nFlags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool IsWindow(IntPtr hWnd);
|
||||
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
||||
public const uint SWP_NOSIZE = 0x0001;
|
||||
public const uint SWP_NOACTIVATE = 0x0010;
|
||||
public const uint SWP_NOZORDER = 0x0004;
|
||||
}
|
||||
"@
|
||||
|
||||
function Clear-Dir([string]$Dir) {
|
||||
if (Test-Path $Dir) {
|
||||
Get-ChildItem -Path $Dir -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
try { [System.IO.File]::Delete($_.FullName) } catch {}
|
||||
}
|
||||
} else { New-Item -ItemType Directory -Path $Dir | Out-Null }
|
||||
}
|
||||
|
||||
# Muta fereastra vfp9 off-screen (x=-4000), fara sa o activeze/minimizeze - ramane randabila
|
||||
# pentru PrintWindow, dar nu se vede si nu fura focus. Se cheama repetat (VFP isi reactiveaza
|
||||
# singur fereastra la Show()/dialoguri modale).
|
||||
function Push-Offscreen([IntPtr]$Hwnd) {
|
||||
if ($Hwnd -eq [IntPtr]::Zero) { return }
|
||||
if (-not [Win32Ui]::IsWindow($Hwnd)) { return }
|
||||
[Win32Ui]::SetWindowPos($Hwnd, [Win32Ui]::HWND_BOTTOM, -4000, 0, 0, 0, ([Win32Ui]::SWP_NOSIZE -bor [Win32Ui]::SWP_NOACTIVATE)) | Out-Null
|
||||
}
|
||||
|
||||
# Polleaza MainWindowHandle pana devine nenul (fereastra principala vfp9 aparuta).
|
||||
function Wait-MainWindowHandle([System.Diagnostics.Process]$Proc, [int]$TimeoutSec) {
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
try {
|
||||
$Proc.Refresh()
|
||||
if ($Proc.HasExited) { return [IntPtr]::Zero }
|
||||
if ($Proc.MainWindowHandle -ne [IntPtr]::Zero) { return $Proc.MainWindowHandle }
|
||||
} catch {}
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
return [IntPtr]::Zero
|
||||
}
|
||||
|
||||
function Wait-File([string]$Path, [int]$TimeoutSec, [IntPtr]$Hwnd = [IntPtr]::Zero) {
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
||||
while (-not (Test-Path $Path)) {
|
||||
if ((Get-Date) -gt $deadline) { return $false }
|
||||
if ($Hwnd -ne [IntPtr]::Zero) { Push-Offscreen $Hwnd }
|
||||
Start-Sleep -Milliseconds 300
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
# Captura pe HANDLE cu PrintWindow (nu depinde de foreground/ecran vizibil). flag 2 =
|
||||
# PW_RENDERFULLCONTENT (randare completa, necesar pt. multe ferestre GDI+/themed); daca da
|
||||
# imagine goala, se incearca fallback cu flag 0.
|
||||
function Take-Screenshot([IntPtr]$Hwnd, [string]$Path) {
|
||||
if ($Hwnd -eq [IntPtr]::Zero -or -not [Win32Ui]::IsWindow($Hwnd)) {
|
||||
Write-Warning "Take-Screenshot: handle invalid, sar captura ($Path)."
|
||||
return
|
||||
}
|
||||
$rect = New-Object Win32Ui+RECT
|
||||
[Win32Ui]::GetWindowRect($Hwnd, [ref]$rect) | Out-Null
|
||||
$w = $rect.Right - $rect.Left
|
||||
$h = $rect.Bottom - $rect.Top
|
||||
if ($w -le 0 -or $h -le 0) { $w = 1920; $h = 1080 }
|
||||
$bmp = New-Object System.Drawing.Bitmap $w, $h
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$hdc = $g.GetHdc()
|
||||
$ok = [Win32Ui]::PrintWindow($Hwnd, $hdc, 2)
|
||||
$g.ReleaseHdc($hdc)
|
||||
$g.Dispose()
|
||||
if (-not $ok) {
|
||||
$g2 = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$hdc2 = $g2.GetHdc()
|
||||
[Win32Ui]::PrintWindow($Hwnd, $hdc2, 0) | Out-Null
|
||||
$g2.ReleaseHdc($hdc2)
|
||||
$g2.Dispose()
|
||||
}
|
||||
$bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bmp.Dispose()
|
||||
}
|
||||
|
||||
# omoara doar vfp9 proprii (linia de comanda contine folderul testului), nu instante straine
|
||||
function Stop-VfpProprii([string]$Marker) {
|
||||
Get-CimInstance Win32_Process -Filter "Name='vfp9.exe'" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
if ($_.CommandLine -and $_.CommandLine.ToLower().Contains($Marker.ToLower())) {
|
||||
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clear-Dir $SyncDir
|
||||
Clear-Dir $ShotsDir
|
||||
|
||||
# 1) Precompilare izolata (proces copil complet, ca sa nu otraveasca sesiunea de lansare).
|
||||
# -WindowStyle Hidden pe consola powershell copil (sigur - e consola, nu GUI VFP).
|
||||
Write-Host "Precompilez .fxp (izolat): $Precompile -Prg $TestPrg"
|
||||
Stop-VfpProprii $TestDir
|
||||
Start-Sleep -Milliseconds 500
|
||||
$pc = Start-Process powershell -WindowStyle Hidden -ArgumentList @('-ExecutionPolicy','Bypass','-File', "`"$Precompile`"", '-Prg', "`"$TestPrg`"") -Wait -PassThru
|
||||
Start-Sleep -Seconds 2
|
||||
Stop-VfpProprii $TestDir
|
||||
Start-Sleep -Seconds 2
|
||||
if (-not (Test-Path $TestFxp)) { Write-Warning "Nu s-a generat $TestFxp - compilare esuata." }
|
||||
|
||||
# 2) Lansez .FXP-ul (nu .prg-ul). vfp9 -A porneste uneori procesul FARA sa execute (log gol);
|
||||
# discriminator: START e prima linie a testului (mtime log > momentul lansarii) => in ~2s daca
|
||||
# ruleaza. Relansez doar daca log-ul nu e scris in 30s (instanta moarta). Apoi astept ready_0.
|
||||
# Imediat ce apare MainWindowHandle, fereastra e impinsa off-screen (fara activare).
|
||||
$ready0 = Join-Path $SyncDir 'ready_0.txt'
|
||||
$started = $false
|
||||
$hwnd = [IntPtr]::Zero
|
||||
foreach ($try in 1..8) {
|
||||
if (Test-Path $ready0) { [System.IO.File]::Delete($ready0) }
|
||||
if (Test-Path $TestLog) { [System.IO.File]::Delete($TestLog) }
|
||||
$t0 = Get-Date
|
||||
Write-Host "Lansez VFP (incercarea $try): $Vfp -A $TestFxp"
|
||||
$proc = Start-Process -FilePath $Vfp -ArgumentList @('-A', "`"$TestFxp`"") -PassThru
|
||||
$dl = (Get-Date).AddSeconds(30)
|
||||
while ((Get-Date) -lt $dl) {
|
||||
if ($hwnd -eq [IntPtr]::Zero -and -not $proc.HasExited) {
|
||||
$proc.Refresh()
|
||||
if ($proc.MainWindowHandle -ne [IntPtr]::Zero) {
|
||||
$hwnd = $proc.MainWindowHandle
|
||||
Push-Offscreen $hwnd
|
||||
}
|
||||
} else {
|
||||
Push-Offscreen $hwnd
|
||||
}
|
||||
if ((Test-Path $TestLog) -and ((Get-Item $TestLog).LastWriteTime -gt $t0)) { $started = $true; break }
|
||||
if (Test-Path $ready0) { $started = $true; break }
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
if ($started) { break }
|
||||
Write-Warning "Incercarea ${try}: testul nu a scris START in 30s (instanta moarta); relansez."
|
||||
if ($proc -and -not $proc.HasExited) { try { $proc.Kill() } catch {} }
|
||||
Stop-VfpProprii $TestDir
|
||||
Start-Sleep -Milliseconds 1500
|
||||
$hwnd = [IntPtr]::Zero
|
||||
}
|
||||
# Daca handle-ul principal inca nu a aparut (formular aditional creat mai tarziu), mai astept putin.
|
||||
if ($hwnd -eq [IntPtr]::Zero) { $hwnd = Wait-MainWindowHandle $proc 15 }
|
||||
Push-Offscreen $hwnd
|
||||
if (-not (Wait-File $ready0 $ReadyTimeoutSec $hwnd)) {
|
||||
Write-Warning "Testul nu a ajuns la ready_0 in ${ReadyTimeoutSec}s. Ultima linie log: '$(Get-Content $TestLog -Tail 1 -ErrorAction SilentlyContinue)'"
|
||||
}
|
||||
|
||||
# 3) Parcurg pasii: astept ready_<n> (re-impingand fereastra off-screen periodic), screenshot
|
||||
# pe handle (PrintWindow), scriu cont_<n>.
|
||||
$ok = $true
|
||||
for ($n = 0; $n -lt $Steps.Count; $n++) {
|
||||
$label = $Steps[$n]
|
||||
$ready = Join-Path $SyncDir ("ready_{0}.txt" -f $n)
|
||||
$cont = Join-Path $SyncDir ("cont_{0}.txt" -f $n)
|
||||
Write-Host "Astept pasul $n ($label)"
|
||||
if (-not (Wait-File $ready $StepTimeoutSec $hwnd)) {
|
||||
Write-Warning "TIMEOUT la pasul $n."
|
||||
$ok = $false
|
||||
break
|
||||
}
|
||||
Write-Host (" ready: {0}" -f (Get-Content $ready -Raw).Trim())
|
||||
# daca fereastra principala s-a schimbat intre timp (formular nou), reincerc handle-ul
|
||||
if ($hwnd -eq [IntPtr]::Zero -or -not [Win32Ui]::IsWindow($hwnd)) { $hwnd = Wait-MainWindowHandle $proc 5 }
|
||||
Push-Offscreen $hwnd
|
||||
Start-Sleep -Milliseconds 1200 # las formularul sa se deseneze complet
|
||||
Push-Offscreen $hwnd
|
||||
$png = Join-Path $ShotsDir ("step_{0}_{1}.png" -f $n, $label)
|
||||
Take-Screenshot $hwnd $png
|
||||
Write-Host " screenshot: $png"
|
||||
Set-Content -Path $cont -Value 'go' -Encoding ascii
|
||||
}
|
||||
|
||||
# 4) Astept done.txt si inchid.
|
||||
if ($ok) {
|
||||
$done = Join-Path $SyncDir 'done.txt'
|
||||
if (Wait-File $done 30 $hwnd) { Write-Host "done: $((Get-Content $done -Raw).Trim())" }
|
||||
}
|
||||
Start-Sleep -Milliseconds 800
|
||||
if ($proc -and -not $proc.HasExited) { try { $proc.Kill() } catch {} }
|
||||
Stop-VfpProprii $TestDir
|
||||
Write-Host "GATA. Screenshots in $ShotsDir"
|
||||
Reference in New Issue
Block a user