Files
comun/docs/depanare_testare_vfp.md
Marius Mutu 859e67e874 Cache ANAF pe ISTORIC_CODURI_FISCALE: verificare din memorie, plasa la tacere ANAF
Verificarile de partener nu mai depind exclusiv de raspunsul ANAF: se citeste
intai cache-ul din ISTORIC_CODURI_FISCALE (mutat in CONTAFIN_ORACLE), cu
provenienta afisata pe ecrane (sursa / data_sursa) si cu plasa de siguranta
cand ANAF tace. Ordinea cache/ANAF e configurabila; cache-ul expira.

- programe/validare.prg: verificare single si pe loturi peste cache, contract
  404 separat de caderea de serviciu, expirarea cache-ului, corectii la
  verdictul pe firma si la etichetarea CACHE_INDISP.
- programe/ocautare.prg: plasa de siguranta si banda de provenienta la cautarea
  de partener.
- programe/oproceduri_comune.prg: VERIFICA_RTVAI_DATA citeste si cache-ul.
- clase/overificari.vc2: propagarea provenientei in interfata.
- utile/Teste/: harness-uri de testare headless - echivalenta cache/ANAF pe 200
  de coduri reale, marginile pe inactiv si TVA la incasare, mock batch ANAF,
  baseline D394 si sondele de diagnostic ANAF.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JCzVWfqscv1xyo5uvJiWiW
2026-08-02 01:33:18 +03:00

11 KiB

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. Daca nu-l stergi, VFP ruleaza codul vechi din .fxp fara niciun semn - testul poate trece si raportul poate parea valid, desi modificarea nu a fost executata; se prinde doar dupa ce lipseste din log o linie pe care o scrie doar codul nou.
  • 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 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.
  • Linie de COD (nu doar literal string) prea lunga intr-o metoda de clasa .vc2 poate arunca eroarea 11 ("Function argument value, type, or count is invalid") pe PRIMA instructiune a metodei, nu pe linia vinovata - simptom complet derutant. Se exclude prin diagnostic: aceeasi expresie merge normal la nivel de program, in alta clasa, intr-o subclasa cu metoda noua, si pe instanta virgina - deci nu tine de tipul datelor, de context sau de clasa parinte. Reper practic: linii preexistente >200 caractere intr-o clasa mare functioneaza pana la ~250; pragul real e in jur de 255, ca la literalii de string. Remediu: sparge expresia in pasi cu variabile locale. Verificare (comparat intre fisierul curent si un backup anterior):
    $l=[IO.File]::ReadAllLines($p,[Text.Encoding]::GetEncoding(28591))
    for($i=0;$i -lt $l.Length;$i++){ if($l[$i].Length -gt 200){ "{0}: len={1}" -f ($i+1), $l[$i].Length } }
    
  • Un UDF care citeste campul curent (Nvl(camp,0)=1 sau similar) nu e de incredere intr-o clauza de filtrare (SELECT ... WHERE, LOCATE FOR, SCAN FOR, DELETE FOR) - pointerul nu e garantat pe randul evaluat la fiecare apel, deci filtrul devine practic o valoare constanta (fie nu se declanseaza niciodata, fie loveste tot). Simptome vazute: o comasare de randuri care nu se mai producea deloc; un test agatat. Remediu: in clauze de filtrare foloseste expresia INLINE pe camp (Nvl(camp,0) <> 1); pastreaza helper-ul doar in cod procedural (If, Replace pe randul curent), unde pointerul e garantat pozitionat.

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.