Files
comun/docs/depanare_testare_vfp.md
Marius Mutu 95cf54e447 Verificare ANAF: raspunsul 404 citit, intrerupator cand serviciul nu raspunde
Serviciul ANAF intoarce HTTP 404 cu corp complet pentru codurile inexistente
({"found":[],"notFound":[...]}), iar wrapper-ul citea corpul doar pe 200 - deci
verdictul "cod inexistent" nu aparea niciodata. Corpul se citeste acum la orice
status, iar verdictul se da doar cand notFound contine chiar codul interogat;
orice alt corp neinteles inseamna "nu a raspuns", nu acuzatie.

Timeout-uri reale pe apelurile web (2/2/3/3): garda Pemstatus din jurul lui
SetTimeouts intoarce .F. pe obiectul COM legat tarziu, deci timeout-urile nu se
aplicau, iar o gazda care inghite pachetele bloca interfata ~21 s per rand.
Aceleasi timeout-uri si pe drumul batch. Fallback-ul Microsoft.XMLHTTP, fara
timeout, a fost scos de pe calea single.

Cand serviciul nu raspunde, verificarile se opresc 10 minute si banda arata ca
au fost sarite, in loc sa taca. La deschiderea formularului pleaca o sonda
asincrona de disponibilitate, cu termen de viata, guard de reintrare si Abort()
la inchidere; randul care asteapta verdictul nu face apel propriu. Cache doar pe
rezultate pozitive. Codul fiscal nenumeric primeste mesaj propriu, in banda si
in detaliile F4.

ParseJsonANAFv8 restaureaza formatul datei si pe calea de eroare (o exceptie
lasa toata sesiunea pe YMD).

Teste: suita headless 70/70, cu sonda si clasele de esec mock-uite (fara retea)
si fixture peste corpurile 404 masurate pe serviciul real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhJwsrFFe1ZNAAtS8Twuti
2026-07-27 16:25:02 +03:00

131 lines
8.9 KiB
Markdown

# Depanare si testare VFP fara IDE (headless)
Metoda de depanare a bug-urilor runtime VFP, valabila pentru toate proiectele ROA.
Complementara cu `flux-editare-vfp-text.md` (editare .vcx/.scx pe text), `cautare_vcx_vct.md`
(cautare in binare) si `testare-ui-vfp.md` (teste cu formular vizibil + screenshots).
Reguli de livrare: `reguli_lucru.md`.
## 1. Fisiere de mediu si exemple
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO, parola
schemei `ROMFASTSOFT`; user aplicatie "MARIUS M" / "123"). Seteaza PATH/CLASSLIB/PROCEDURE
ca programul principal si instantiaza goConn/goExecutor/goApp/goLog/goFirma/goCalendar +
pachetele de sesiune Oracle. Dupa apel poti face `CREATEOBJECT('frm_xxx')` si apela metodele
punctual. Varianta interactiva (INPUTBOX + meniu): `COMUN\programe\test_init_env.prg`.
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - harness model: init mediu -> replicare
flux -> instantiere forma reala -> executie pas cu pas cu logare -> verdict OK/BUG.
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness fara Oracle (cursori + goExecutor
dummy) cu parametru care incarca o copie de clasa din scratchpad, pentru bisectie A/B.
## 2. Rulare headless
`vfp9.exe -c<config.fpw> -A -T "<script.prg>" <param>`, lansat din PowerShell cu timeout:
```
$p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru
$p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() }
```
- `-A` ignora config.fpw implicit, `-T` sare peste splash; **ambele obligatorii** - fara `-T`
procesul ramane pe ecranul de intampinare (simptom identic cu o eroare de compilare).
Un `-c<cale>` explicit (lipit de flag) e preluat chiar si impreuna cu `-A`.
- **SAFETY**: sub `-A` porneste ON, deci primul `STRTOFILE` peste un log existent (sau
`COMPILE` peste un `.err`/`.fxp` existent) scoate dialogul modal "overwrite?" si procesul
pare agatat. Pune `SET SAFETY OFF` ca PRIMA linie din script si/sau `SAFETY=OFF` +
`RESOURCE=OFF` intr-un config.fpw propriu pasat cu `-c`. Verificare: logheaza
`SET("SAFETY")`/`SET("RESOURCE")` la start.
- In script: logare cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
`ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza si
continua (mimeaza ErrorHandler); `QUIT` obligatoriu la final.
- `LINENO()` din handler e relativ la inceputul metodei: linia in `.vc2` = linia raportata +
linia `PROCEDURE`.
- Nu rula cu IDE-ul sau exe-ul aplicatiei deschis (lock pe binare); sterge `.fxp`-ul vechi
daca ai editat scriptul intre rulari.
- Forme modale: `Show(1)` blocheaza. Replica in script doar liniile relevante din Show, sau
seteaza `WindowType = 0`.
## 3. Citirea log-ului aplicatiei (log.txt din radacina proiectului)
`ErrorHandler` scrie `nErrror`, `cMethod`, `line` (0 daca exe-ul e fara debug info) si stack-ul.
Semantica ON ERROR, esentiala la interpretare:
- Dupa RETURN din handler executia CONTINUA de la linia urmatoare => o singura cauza produce
CASCADE de erori. Tipar clasic: eroare pe `WITH <expr>` => liniile `.prop=` dau 1940
"Expression is not valid outside of WITH/ENDWITH", `ENDWITH` da 1939. **Prima eroare din
secventa e cea reala**, restul e zgomot.
- Erorile din TRY/CATCH nu ajung in log. Un CATCH gol poate ascunde ani un defect latent care
"explodeaza" cand codul din jur se repara.
- Liniile "ON..." din stack sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
## 4. Verificare de sintaxa prin compilare headless (dupa orice editare de `.prg`)
Cel mai ieftin test dupa o modificare: `COMPILE` headless, fara aplicatie si fara Oracle.
- Compileaza o COPIE a fisierului in scratchpad, nu sursa din working copy (`COMPILE` scrie
`.fxp` langa `.prg`, care ar aparea ca modificare in SVN). VFP nu rezolva simboluri externe
la compilare, deci copia izolata e suficienta pentru sintaxa.
- Sterge `.err`-ul vechi INAINTE de `COMPILE`; verdictul = existenta lui `<nume>.err` dupa
(exista => logheaza continutul; nu exista => compilare curata).
- Nu inlocuieste testul functional: prinde sintaxa, nu apeluri catre metode inexistente.
## 5. Bisectia versiunilor de clase (regresii)
Cand un bug apare "de azi", compara cu clasa veche fara a atinge SVN:
1. `git show <rev>:clase\fisier.vcx > temp\fisier.vcx` (+ `.vct`); pentru diff lizibil,
`vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`.
2. In harness incarca DOAR copia (`SET CLASSLIB TO temp\fisier.vcx`), **nu ADDITIVE peste cea
reala** - rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ.
3. Fara git, din backup-urile de runda: copiaza `*.vc2.pre_rundaN.bak` intr-un temp si
regenereaza binarul cu `FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune
`..\include\foxpro.h` langa, altfel `.ERR`).
4. Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource -> camp
simplu, sterge linii de proprietate) - NU sterge obiecte/coloane din `ADD OBJECT` (strica
structura: "Cannot add this object to a Grid").
5. Cand diferenta apare dupa un fix "inofensiv" in COMUN, cauza poate fi un defect latent
activat de fix (cod care nu rulase niciodata incepe sa ruleze), nu diff-ul in sine.
## 6. Capcane VFP (de verificat la bug-uri asemanatoare)
- **`SELECT ... INTO CURSOR` NU mosteneste indecsii sursei.** Orice `Seek(...,'cursor','tag')`
pe copie crapa; recreeaza indexul dupa copiere. Acelasi lucru pentru cursorii creati intr-un
test: trebuie sa aiba EXACT indecsii pe care ii creeaza fluxul real inainte de instantiere,
altfel ordinea/logica difera si testul da rezultat fals.
- **Un grid isi pierde TOATE coloanele** (`ColumnCount` devine 0, fara eroare in log) daca un
ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse programatic (ex. font per
coloana la Init). Simptome in aval: `attachtogrid` copiaza 0 coloane, apoi `calctotal` da
"Property <cColWithLabel> is not found". Similar, un cursor legat la grid recreat de Init
lasa grid-ul fara coloane (eroare 1925).
- **ControlSource-uri cu campuri inexistente sunt tolerate la afisare**, dar orice expresie
care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie nedefinita) declanseaza cazul
de mai sus. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`.
- **Coloana de grid cu ControlSource EXPRESIE (nu camp) cere `Bound = .F.`** Fara el: eroare 9
"Data type mismatch" la `CREATEOBJECT`-ul formei (faza de constructie, raportata la linia
apelanta, Details gol).
- **Erorile de instantiere sunt raportate la linia `CREATEOBJECT`, nu la `.Init`**: daca
`Procedure` = scriptul apelant, eroarea vine din evaluarea definitiei clasei (`ADD OBJECT ...
WITH RowSource`, ControlSource-expresie), pentru ca bind-ul controalelor native se rezolva la
CONSTRUCTIE, inainte de `Init`.
- **Literal string > 255 caractere = eroare de COMPILARE, nu de runtime** (masurat 24.07.2026:
255 trece, 256 pica). Mesaj: `Unrecognized command verb` daca linia incepe cu un apel de
metoda, `Command contains unrecognized phrase/keyword` la o atribuire. Insidios: restul
programului ruleaza, doar linia aceea lipseste din `.fxp` si da eroare 16 cand se ajunge la
ea. De aceea SQL-ul lung se scrie cu `TEXT TO <var> [TEXTMERGE] NOSHOW ... ENDTEXT` sau
concatenat. Verificare: `awk 'length($0) > 260 {print NR": "length($0)}' fisier.prg`.
## 7. Capcane la SCRIEREA scriptului de test
- **`SET SAFETY OFF` in primele linii, INAINTE de orice `STRTOFILE`/`CREATE CURSOR`** (plus
`SET TALK OFF`). Fara el VFP deschide dialogul de confirmare la suprascrierea fisierului de
log, iar headless dialogul blocheaza procesul pana la timeout. Suitele existente il au deja -
capcana apare la scripturile ad-hoc, scrise repede pentru o masuratoare.
- **`DEFINE CLASS ... ENDDEFINE` nu poate sta la mijlocul programului principal.** Toate liniile
de DUPA `ENDDEFINE` dau "Statement is not in a procedure" (vezi `<script>.ERR`); scriptul
ruleaza doar pana acolo si se opreste silentios, fara sa ajunga la `QUIT` - simptom: vfp9.exe
pare agatat la nesfarsit. Pune orice `DEFINE CLASS` auxiliar la FINALUL fisierului.
- **Dummy-uri cu metode**: `CREATEOBJECT('Custom')` + `AddProperty()` adauga doar PROPRIETATI.
Daca clasa testata apeleaza `goExecutor.oExecute(...)`, dummy-ul trebuie sa fie
`DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute` reala.
- **Diagnostic "script agatat"**, in ordine: (1) exista `<script>.ERR` langa `.prg`? => s-a
oprit la compilare; (2) CPU-ul procesului ~0 dupa cateva secunde (`Get-Process | select CPU`)
=> asteapta un dialog modal, nu proceseaza; confirma enumerand ferestrele procesului cu
`EnumWindows`/`GetWindowText`/`GetWindowThreadProcessId` (`Add-Type`, user32) filtrat pe PID -
daca vezi doar fereastra principala + "Command", nu e dialog, cauta alta cauza (tipic `-T`
lipsa). Confirma mereu cauza inainte de a schimba scriptul.