docs: compactare depanare/testare VFP (dedup + eliminare cazuri specifice)

depanare_testare_vfp.md 226->118 linii, testare-ui-vfp.md 133->90.
- regula SAFETY/dialog modal si -A -T erau enuntate de 3 ori -> o data
- cele 3 capcane "proces agatat" -> un singur algoritm de diagnostic in 3 pasi
- studiul de caz CNRCRT si detaliile ROAGEST (jtva_coloane2, achizitie_import)
  reduse la regula generala + pointer catre scriptul de test
- reguli_lucru pct.4 ramane doar pointer, fara detaliile duplicate de mediu
- sters docs/bash.exe.stackdump (fisier de gunoi, negitat)
Masuratorile si datele concrete pastrate integral.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqJ7D5ftMbChDyz5mpVkCd
This commit is contained in:
2026-07-24 10:59:30 +03:00
parent 22e97a9225
commit ca465641e8
3 changed files with 209 additions and 348 deletions

View File

@@ -1,225 +1,126 @@
# Depanare si testare VFP fara IDE (headless) # Depanare si testare VFP fara IDE (headless)
Metoda folosita la depanarea erorii "Property CNRCRT is not found" (ROACONT 2.11.61, Metoda de depanare a bug-urilor runtime VFP, valabila pentru toate proiectele ROA.
15.07.2026) - reutilizabila pentru orice bug runtime VFP. Complementara cu Complementara cu `flux-editare-vfp-text.md` (editare .vcx/.scx pe text), `cautare_vcx_vct.md`
`flux-editare-vfp-text.md` (editare .vcx/.scx pe text) si `cautare_vcx_vct.md` (cautare). (cautare in binare) si `testare-ui-vfp.md` (teste cu formular vizibil + screenshots).
Reguli de livrare: `reguli_lucru.md`.
## 0. Reguli de lucru
## 1. Fisiere de mediu si exemple
Vezi `reguli_lucru.md` (diff ca fisier + aprobare inainte de write-back, comentarii
max o linie, modificari minime). - `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO, parola
## Fisiere de testare (foloseste-le la orice depanare/testare viitoare) schemei `ROMFASTSOFT`; user aplicatie "MARIUS M" / "123"). Seteaza PATH/CLASSLIB/PROCEDURE
ca programul principal si instantiaza goConn/goExecutor/goApp/goLog/goFirma/goCalendar +
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA pachetele de sesiune Oracle. Dupa apel poti face `CREATEOBJECT('frm_xxx')` si apela metodele
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO). punctual. Varianta interactiva (INPUTBOX + meniu): `COMUN\programe\test_init_env.prg`.
Echivalentul headless al `COMUN\programe\test_init_env.prg` (care ramane varianta - `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - harness model: init mediu -> replicare
interactiva, cu INPUTBOX si meniu de lansare formulare). flux -> instantiere forma reala -> executie pas cu pas cu logare -> verdict OK/BUG.
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - exemplu complet de harness: - `COMUN\utile\Teste\test_repro_import_nota.prg` - harness fara Oracle (cursori + goExecutor
init mediu -> replicare flux (cursoarele din modificanote) -> instantiere forma reala -> dummy) cu parametru care incarca o copie de clasa din scratchpad, pentru bisectie A/B.
executie pas cu pas cu logare -> verdict OK/BUG. Copiaza-i structura pentru bug-uri noi.
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness instantiere import_nota ## 2. Rulare headless
(ROAGEST) fara Oracle: cursori dummy + goExecutor dummy; parametrul LPARAMETERS
incarca o copie de clasa din scratchpad in loc de cea reala (bisectie A/B). `vfp9.exe -c<config.fpw> -A -T "<script.prg>" <param>`, lansat din PowerShell cu timeout:
- `COMUN\utile\Teste\teste_roacont.prg` si `COMUN\utile\Teste\test.prg` - teste ad hoc istorice. ```
- Mediu de test standard: server `CENTRAL`, schema `MARIUSM_AUTO`, parola schemei $p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru
`ROMFASTSOFT`; utilizator de aplicatie "MARIUS M" / parola "123" (unde e nevoie de login). $p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() }
```
## 1. Citirea log-ului aplicatiei (log.txt in radacina proiectului) - `-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).
`ErrorHandler` (roacont.prg) scrie pentru fiecare eroare: `nErrror`, `cMethod`, Un `-c<cale>` explicit (lipit de flag) e preluat chiar si impreuna cu `-A`.
`line` (0 daca exe-ul e compilat fara debug info) si stack-ul complet. - **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
Semantica ON ERROR (esentiala la interpretare): pare agatat. Pune `SET SAFETY OFF` ca PRIMA linie din script si/sau `SAFETY=OFF` +
- Dupa ce handlerul face RETURN, executia CONTINUA de la linia urmatoare din metoda `RESOURCE=OFF` intr-un config.fpw propriu pasat cu `-c`. Verificare: logheaza
care a crapat. O singura cauza produce deci CASCADE de erori. `SET("SAFETY")`/`SET("RESOURCE")` la start.
- Tipar clasic: eroare pe linia `WITH <expr>` => urmatoarele linii `.prop = ...` dau - In script: logare cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
1940 "Expression is not valid outside of WITH/ENDWITH", apoi `ENDWITH` da `ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza si
1939 "WITH/ENDWITH mismatch". PRIMA eroare din secventa e cea reala; restul e zgomot. continua (mimeaza ErrorHandler); `QUIT` obligatoriu la final.
- Erorile din interiorul TRY/CATCH nu ajung in log (inghitite). Un CATCH gol poate - `LINENO()` din handler e relativ la inceputul metodei: linia in `.vc2` = linia raportata +
ascunde ani de zile un defect latent, care "explodeaza" cand codul din jur se repara linia `PROCEDURE`.
(exact cazul typo-ului oMinHeaderHeight din cusfonthandler). - Nu rula cu IDE-ul sau exe-ul aplicatiei deschis (lock pe binare); sterge `.fxp`-ul vechi
- In stack, liniile "ON..." sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale. daca ai editat scriptul intre rulari.
- Forme modale: `Show(1)` blocheaza. Replica in script doar liniile relevante din Show, sau
## 2. Rulare headless a scripturilor VFP seteaza `WindowType = 0`.
Sablonul unui script de diagnostic (vezi `utile\Teste\test_repro_cnrcrt_footer.prg`): ## 3. Citirea log-ului aplicatiei (log.txt din radacina proiectului)
- logare in fisier cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
- `ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza `ErrorHandler` scrie `nErrror`, `cMethod`, `line` (0 daca exe-ul e fara debug info) si stack-ul.
si continua - mimeaza comportamentul ErrorHandler din aplicatie; Semantica ON ERROR, esentiala la interpretare:
- `QUIT` obligatoriu la final (altfel ramane vfp9.exe agatat); - Dupa RETURN din handler executia CONTINUA de la linia urmatoare => o singura cauza produce
- lansare din PowerShell cu timeout: CASCADE de erori. Tipar clasic: eroare pe `WITH <expr>` => liniile `.prop=` dau 1940
`$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() }` "Expression is not valid outside of WITH/ENDWITH", `ENDWITH` da 1939. **Prima eroare din
- `-A` ignora config, `-T` fara ecran de start; parametrul dupa .prg ajunge in LPARAMETERS. secventa e cea reala**, restul e zgomot.
- LINENO() din handler e relativ la inceputul metodei (utile pentru localizare in .vc2: - Erorile din TRY/CATCH nu ajung in log. Un CATCH gol poate ascunde ani un defect latent care
linia din metoda = linia raportata + linia PROCEDURE din .vc2). "explodeaza" cand codul din jur se repara.
- Liniile "ON..." din stack sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
Precautii: nu rula cu VFP IDE sau exe-ul aplicatiei deschis (lock pe binare);
sterge .fxp-ul vechi de langa .prg daca ai editat scriptul intre rulari. ## 4. Verificare de sintaxa prin compilare headless (dupa orice editare de `.prg`)
## 3. Mediu complet cu Oracle, fara interactiune 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
`utile\Teste\test_init_env_auto.prg` = varianta neinteractiva a `.fxp` langa `.prg`, care ar aparea ca modificare in SVN). VFP nu rezolva simboluri externe
`programe\test_init_env.prg` (fara INPUTBOX/xmenu): primeste host/schema/parola ca la compilare, deci copia izolata e suficienta pentru sintaxa.
parametri, seteaza PATH/CLASSLIB/PROCEDURE ca roacont.prg, instantiaza - Sterge `.err`-ul vechi INAINTE de `COMPILE`; verdictul = existenta lui `<nume>.err` dupa
goConn/goExecutor/goApp/goLog, incarca goFirma/goCalendar/optiuni si pachetele de (exista => logheaza continutul; nu exista => compilare curata).
sesiune Oracle. Dupa apel poti instantia forme reale (`CREATEOBJECT('frm_xxx', ...)`) - Nu inlocuieste testul functional: prinde sintaxa, nu apeluri catre metode inexistente.
si apela metodele lor punctual, cu cursoarele pregatite ca in fluxul real.
## 5. Bisectia versiunilor de clase (regresii)
Pentru forme care se deschid modal (Show(1) blocheaza), nu apela Show: replica in
script doar liniile relevante din Show (attach, calcule) si logheaza starea intre ele. 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,
## 4. Bisectia versiunilor de clase (regresii) `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
Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge SVN: reala** - rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ.
1. `git show <rev_vechi>:clase/fisier.vcx > temp\fisier.vcx` (+ .vct identic); 3. Fara git, din backup-urile de runda: copiaza `*.vc2.pre_rundaN.bak` intr-un temp si
2. convertit la text pentru diff: `vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`; regenereaza binarul cu `FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune
3. in scriptul de test, `SET CLASSLIB TO temp\fisier.vcx ADDITIVE` (varianta veche) `..\include\foxpro.h` langa, altfel `.ERR`).
vs. cea din COMUN (varianta noua) - A/B pe acelasi flux; 4. Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource -> camp
4. cand difera rezultatul, cauza e in diff-ul acelei clase (sau intr-un defect latent simplu, sterge linii de proprietate) - NU sterge obiecte/coloane din `ADD OBJECT` (strica
pe care noul cod il activeaza - vezi capcanele de mai jos). structura: "Cannot add this object to a Grid").
5. Cand diferenta apare dupa un fix "inofensiv" in COMUN, cauza poate fi un defect latent
Varianta pe backup-uri de runda (fara git): copiaza `*.vc2.pre_rundaN.bak` din cache activat de fix (cod care nu rulase niciodata incepe sa ruleze), nu diff-ul in sine.
intr-un folder temp ca `ointroduceri.vc2`, regenereaza binarul direct cu
`FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune `..\include\foxpro.h` langa, ## 6. Capcane VFP (de verificat la bug-uri asemanatoare)
altfel .ERR), apoi in harness incarca DOAR copia (nu ADDITIVE peste cea reala -
rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ). - **`SELECT ... INTO CURSOR` NU mosteneste indecsii sursei.** Orice `Seek(...,'cursor','tag')`
Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource -> pe copie crapa; recreeaza indexul dupa copiere. Acelasi lucru pentru cursorii creati intr-un
camp simplu, sterge linii de proprietate din mijlocul blocului) - NU sterge test: trebuie sa aiba EXACT indecsii pe care ii creeaza fluxul real inainte de instantiere,
obiecte/coloane din ADD OBJECT (strica structura: "Cannot add this object to a Grid"). altfel ordinea/logica difera si testul da rezultat fals.
- **Un grid isi pierde TOATE coloanele** (`ColumnCount` devine 0, fara eroare in log) daca un
## 5. Capcane VFP descoperite (de verificat la bug-uri asemanatoare) 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
- `SELECT ... INTO CURSOR` NU mosteneste indecsii cursorului sursa. Orice "Property <cColWithLabel> is not found". Similar, un cursor legat la grid recreat de Init
`Seek(..., 'cursor', 'tag')` pe copie crapa. `update_jtva_coloane` creeaza lasa grid-ul fara coloane (eroare 1925).
`INDEX ON id_jtva_coloana TAG id_jtva`; daca ii copiezi rezultatul, recreeaza indexul. - **ControlSource-uri cu campuri inexistente sunt tolerate la afisare**, dar orice expresie
- Un grid isi pierde TOATE coloanele (ColumnCount devine 0, fara nicio eroare in log) care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie nedefinita) declanseaza cazul
daca un ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse de mai sus. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`.
programatic (ex. FontName/FontSize per coloana in cusfonthandler.ApplyFontToGrid, - **Coloana de grid cu ControlSource EXPRESIE (nu camp) cere `Bound = .F.`** Fara el: eroare 9
la Init-ul formei prin _frmbase.SetFont). Simptome tipice in aval: "Data type mismatch" la `CREATEOBJECT`-ul formei (faza de constructie, raportata la linia
`_grdfooter.attachtogrid` copiaza 0 coloane, apoi `calctotal` da apelanta, Details gol).
"Property <cColWithLabel> is not found" la `Getpem(This, This.cColWithLabel)`. - **Erorile de instantiere sunt raportate la linia `CREATEOBJECT`, nu la `.Init`**: daca
- ControlSource-uri cu campuri inexistente in RecordSource sunt tolerate la afisare, `Procedure` = scriptul apelant, eroarea vine din evaluarea definitiei clasei (`ADD OBJECT ...
dar orice expresie care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie WITH RowSource`, ControlSource-expresie), pentru ca bind-ul controalelor native se rezolva la
nedefinita) declanseaza comportamentul de mai sus. CONSTRUCTIE, inainte de `Init`.
- Fix-ul unui typo intr-o clasa partajata poate activa defecte latente in aval - **Literal string > 255 caractere = eroare de COMPILARE, nu de runtime** (masurat 24.07.2026:
(codul care nu rulase niciodata incepe sa ruleze). La regresii dupa fix-uri 255 trece, 256 pica). Mesaj: `Unrecognized command verb` daca linia incepe cu un apel de
"inofensive" in COMUN, cauta defectul latent, nu doar diff-ul. metoda, `Command contains unrecognized phrase/keyword` la o atribuire. Insidios: restul
- **Literal string de peste 255 de caractere = eroare de COMPILARE, nu de runtime** (masurat programului ruleaza, doar linia aceea lipseste din `.fxp` si da eroare 16 cand se ajunge la
24.07.2026: 255 trece, 256 pica). Mesajul difera dupa forma liniei: `Unrecognized command ea. De aceea SQL-ul lung se scrie cu `TEXT TO <var> [TEXTMERGE] NOSHOW ... ENDTEXT` sau
verb` daca linia incepe cu un apel de metoda (`goExecutor.oExecuta("<sql lung>", 'c')`), concatenat. Verificare: `awk 'length($0) > 260 {print NR": "length($0)}' fisier.prg`.
`Command contains unrecognized phrase/keyword` la o atribuire (`lc = "<sir lung>"`).
Insidios: restul programului ruleaza normal, doar linia aceea lipseste din `.fxp` si da ## 7. Capcane la SCRIEREA scriptului de test
eroare 16 cand se ajunge la ea - deci un test poate raporta "FAIL" pe altceva decat cauza
reala. De aceea tot SQL-ul lung din codebase e scris cu `TEXT TO <var> [TEXTMERGE] NOSHOW` - **`DEFINE CLASS ... ENDDEFINE` nu poate sta la mijlocul programului principal.** Toate liniile
... `ENDTEXT` sau concatenat din bucati, nu ca literal unic. Verificare rapida: de DUPA `ENDDEFINE` dau "Statement is not in a procedure" (vezi `<script>.ERR`); scriptul
`awk 'length($0) > 260 {print NR": "length($0)}' fisier.prg`. ruleaza doar pana acolo si se opreste silentios, fara sa ajunga la `QUIT` - simptom: vfp9.exe
- **Coloana de grid cu ControlSource EXPRESIE (nu camp) cere `Bound = .F.`**. pare agatat la nesfarsit. Pune orice `DEFINE CLASS` auxiliar la FINALUL fisierului.
Fara el: eroare 9 "Data type mismatch" la CREATEOBJECT-ul formei (faza de - **Dummy-uri cu metode**: `CREATEOBJECT('Custom')` + `AddProperty()` adauga doar PROPRIETATI.
constructie, raportata la linia apelanta, Details gol). Caz: import_nota.GridArt Daca clasa testata apeleaza `goExecutor.oExecute(...)`, dummy-ul trebuie sa fie
Column7/8/9 cu `round(...)` (ROAGEST 16.07.2026); model corect: Grid1.cTaxCode `DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute` reala.
(`Bound = .F.` + expresie, functional de ani). - **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`)
## 6. Capcane la SCRIEREA scriptului de test (nu ale bug-ului investigat) => asteapta un dialog modal, nu proceseaza; confirma enumerand ferestrele procesului cu
`EnumWindows`/`GetWindowText`/`GetWindowThreadProcessId` (`Add-Type`, user32) filtrat pe PID -
Descoperite depanand harness-ul pentru "Data type mismatch" la `CREATEOBJECT('IMPORT_nota',...)` daca vezi doar fereastra principala + "Command", nu e dialog, cauta alta cauza (tipic `-T`
(ROAGEST, 16.07.2026) - de verificat oricand un script de test nou pare "agatat". lipsa). Confirma mereu cauza inainte de a schimba scriptul.
- **`DEFINE CLASS ... ENDDEFINE` NU poate sta la mijlocul programului principal.**
Daca il pui intre alte instructiuni de top-level (ex. clasa dummy pentru un
`goExecutor` de test, definita devreme "ca sa fie la indemana"), VFP compileaza
gresit: TOATE liniile de DUPA `ENDDEFINE` dau "Statement is not in a procedure"
(vezi `<script>.ERR` generat la compilare). Simptom in productie: `vfp9.exe`
pare agatat la nesfarsit - CPU ~0, nicio fereastra de dialog vizibila (verificat
si prin enumerare Win32 a ferestrelor procesului), pentru ca de fapt a rulat
doar liniile de dinaintea `DEFINE CLASS` si s-a oprit silentios dupa eroarea de
compilare, fara sa ajunga la `QUIT`. Fix: muta orice `DEFINE CLASS` auxiliar
(dummy executor, helpere) la FINALUL fisierului, dupa `QUIT`, langa celelalte
`PROCEDURE` - e locul standard pentru cod suport intr-un script de test.
- **Diagnostic rapid compilare vs blocaj real**: cand un script "nu se termina",
verifica INTAI daca a aparut `<script>.ERR` langa `.prg` (log de compilare,
vezi si `roagest.ERR`/`log.txt` in radacina proiectului) - inseamna ca s-a
oprit la compilare, nu ca ruleaza/asteapta ceva. Daca nu exista `.ERR`, verifica
CPU-ul procesului (`Get-Process | select CPU`): ~0 dupa cateva secunde inseamna
ca asteapta un dialog modal (foloseste enumerarea Win32 de ferestre de mai jos
ca sa confirmi), nu ca proceseaza ceva lent.
- **Enumerare ferestre Win32 pentru un proces "agatat"** (utila cand nu esti sigur
daca exista un dialog modal ascuns): `Add-Type` cu `EnumWindows`/`GetWindowText`/
`GetWindowThreadProcessId` din `user32.dll`, filtrat pe PID-ul lui `vfp9.exe`.
Daca vezi doar fereastra principala + "Command", nu e dialog - cauta alta cauza
(vezi punctul de mai sus).
- **`STRTOFILE` inainte de `SET SAFETY OFF` = dialog modal "Overwrite?"** daca
fisierul de log exista deja (sub `-A`, SAFETY porneste ON). Pune `SET SAFETY OFF`
PRIMA linie sau sterge log-ul inainte de fiecare rulare.
- **`-A -T` sunt obligatorii impreuna** la lansare headless (`-A` ignora config.fpw,
`-T` sare peste splash screen-ul de start) - fara `-T` procesul ramane agatat pe
ecranul de intampinare, identic ca simptom cu o eroare de compilare (CPU ~0,
fara log). Confirma mereu care e cauza inainte de a schimba scriptul.
- **`goExecutor`/alte dependinte "dummy"**: `CREATEOBJECT('Custom')` + `AddProperty()`
poate adauga doar PROPRIETATI, nu METODE. Daca clasa testata apeleaza
`goExecutor.oExecute(...)`/`oExecuta(...)`, dummy-ul trebuie sa fie o clasa
proprie (`DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute`/
`oExecuta` reale), nu un `Custom` cu proprietati adaugate.
- **Erorile de instantiere (`CREATEOBJECT`) raportate la linia CREATEOBJECT, nu la
`.Init`**: cand `ON ERROR`/`CATCH` arata `Procedure` = scriptul apelant (nu
`NUMECLASA.INIT`), inseamna ca eroarea vine din evaluarea proprietatilor/
obiectelor din DEFINITIA clasei (`ADD OBJECT ... WITH RowSource = ...`,
`ControlSource` de coloana grid evaluat ca expresie, etc.) - controale native
VFP (ex. combobox cu `RowSourceType=6` Fields) isi rezolva bind-ul la
CONSTRUCTIE, inainte sa apuce sa ruleze `PROCEDURE Init` a formei. Cand
reproduci un astfel de bug cu un mediu minimal, o "Alias is not found" pe un
cursor pe care nu l-ai deschis tu confirma mecanismul (faza de instantiere),
chiar daca nu e mesajul EXACT din bug-ul original.
## 7. Verificare de sintaxa prin compilare headless (dupa orice editare de `.prg`)
Cel mai ieftin test dupa o modificare intr-un `.prg`: `COMPILE` headless, care prinde erorile
de sintaxa fara sa porneasca aplicatia si fara Oracle. Reguli:
- **`config.fpw` propriu cu `SAFETY=OFF` si `RESOURCE=OFF`, pasat cu `-c<cale>`** (lipit de
flag, fara spatiu). Fara el, `COMPILE` peste un `.err`/`.fxp` existent si `STRTOFILE` peste
un log existent scot dialogul modal "overwrite?" si procesul pare agatat. `SET SAFETY OFF`
ca prima linie in script e complementar, nu inlocuitor (`-A` singur porneste cu SAFETY ON).
Verificare ca a fost preluat: logheaza `SET("SAFETY")` / `SET("RESOURCE")` la start.
- **`-c<config>` explicit are prioritate si merge impreuna cu `-A`**: desi `-A` ignora
config.fpw-ul implicit, un `-c` explicit tot e preluat (verificat 24.07.2026:
`vfp9.exe -c<config> -A -T script.prg` a raportat `SAFETY=[OFF] RESOURCE=[OFF]`).
- **Compileaza o COPIE a fisierului, in scratchpad, nu sursa din working copy**: `COMPILE`
scrie `.fxp`-ul langa `.prg`, iar `.fxp`-urile din SVN sunt artefacte de build - regenerate
ad-hoc, ar aparea ca modificari in working copy. VFP compileaza fara sa rezolve simbolurile
(clase, functii din alte fisiere), deci copia izolata e suficienta pentru sintaxa.
- **Sterge `.err`-ul vechi inainte de `COMPILE`**, altfel nu poti distinge un `.err` ramas de
la rularea precedenta de unul nou. Verdictul se citeste din existenta `<nume>.err`:
exista => logheaza continutul lui; nu exista => compilare curata.
Sablon (rulat dupa fiecare runda de editare pe `anaf_efactura.prg`, 24.07.2026):
```
SET SAFETY OFF
lcDir = '<scratchpad>\'
STRTOFILE('START ' + TTOC(DATETIME()) + ' SAFETY=' + SET("SAFETY") + CRLF, lcLog)
IF FILE(lcDir + 'copie.err')
DELETE FILE (lcDir + 'copie.err')
ENDIF
COMPILE (lcDir + 'copie.prg')
IF FILE(lcDir + 'copie.err')
STRTOFILE('ERR:' + CRLF + FILETOSTR(lcDir + 'copie.err'), lcLog, 1)
ELSE
STRTOFILE('OK: compilare fara erori', lcLog, 1)
ENDIF
QUIT
```
Nu inlocuieste testul functional: `COMPILE` valideaza sintaxa, nu apeluri catre metode
inexistente sau proprietati gresite (acelea apar abia la runtime).
## 8. Studiu de caz: "Property CNRCRT is not found" (15.07.2026)
Lant complet: `frm_import_extrase_banca.modificanote` copia `crsJTVATemp` fara index
(defect latent) -> coloana `cExplicatieTVA` din `frm_modific2024.grdRulaje` face
`Seek(trul.id_jtva_coloana,'crsJtvaTemp','id_jtva')` in ControlSource -> fix-ul
typo-ului `oMinHeaderHeight`->`nMinHeaderHeight` in `cusfonthandler` (r17869) a facut
ca ApplyFontToGrid sa atinga coloanele la Init -> grid-ul a ramas cu 0 coloane ->
footer-ul a crapat in calctotal. Fix: `INDEX ON id_jtva_coloana TAG id_jtva` dupa
SELECT INTO in modificanote. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`
(mod FARAINDEX reproduce bug-ul, mod CUINDEX valideaza fix-ul).

View File

@@ -6,11 +6,8 @@
2. Comentarii in cod: max o linie `*!* DD.MM.YYYY autor - motiv scurt`. 2. Comentarii in cod: max o linie `*!* DD.MM.YYYY autor - motiv scurt`.
Explicatiile merg in docs/ sau in mesajul de commit, nu in cod. Explicatiile merg in docs/ sau in mesajul de commit, nu in cod.
3. Modificari minime: doar ce s-a cerut; fara refactorizari sau curatenie din oficiu. 3. Modificari minime: doar ce s-a cerut; fara refactorizari sau curatenie din oficiu.
4. Testare headless (fara IDE): `vfp9.exe -A -T "<script.prg>" <param>` din PowerShell, 4. Testare headless (fara IDE): `vfp9.exe -A -T "<script.prg>" <param>` din PowerShell.
logare cu STRTOFILE, `ON ERROR` care logheaza si continua, QUIT la final. Mediu, sabloane, capcane si metoda de depanare: `depanare_testare_vfp.md`.
Init mediu neinteractiv: `utile\Teste\test_init_env_auto.prg` (CENTRAL / MARIUSM_AUTO;
user aplicatie "MARIUS M"/"123"). Exemplu harness complet: `utile\Teste\test_repro_cnrcrt_footer.prg`.
Metoda de depanare (log.txt, cascade ON ERROR, bisectie clase): `depanare_testare_vfp.md`.
5. Editare .vcx/.scx pe text (cache, cp1252 byte-safe, fidelity-check): `flux-editare-vfp-text.md`. 5. Editare .vcx/.scx pe text (cache, cp1252 byte-safe, fidelity-check): `flux-editare-vfp-text.md`.
Cautare in binare: `cautare_vcx_vct.md`. Cautare in binare: `cautare_vcx_vct.md`.
6. Misiuni lungi (teste, investigatii, sarcini inlantuite): NU un singur subagent cu context 6. Misiuni lungi (teste, investigatii, sarcini inlantuite): NU un singur subagent cu context

View File

@@ -1,132 +1,95 @@
# Testare UI VFP headless cu harness + screenshots # Testare UI VFP headless cu harness + screenshots
Harness reutilizabil pentru teste care afiseaza un formular REAL (fara Oracle/UI complet), Harness reutilizabil pentru teste care afiseaza un formular REAL (cu dependinte dummy, fara
executa actiuni si valideaza vizual prin capturi de ecran. Valabil pentru toate proiectele Oracle), executa actiuni si valideaza vizual prin capturi. Valabil pentru toate proiectele VFP
VFP din suita ROA; exemplele sunt pe ROAGEST. Alternativa la din suita ROA. Alternativa la `testare-vfp-mcp.md` (care porneste programul principal intreg);
`testare-vfp-mcp.md` (care ruleaza programul principal intreg + windows-mcp): aici instantiem o capcanele generale de rulare headless (`-A -T`, SAFETY, script agatat) sunt in
singura clasa cu dependinte dummy, deci pornire rapida si izolata. `depanare_testare_vfp.md`.
Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, parametrizat), Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, parametrizat),
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri VFP de handshake), `_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri de handshake),
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele de `mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele stau in
teste stau grupate pe subiect in subfoldere (ex. `achizitie_import\`). Exemplu complet: subfoldere pe subiect. Exemplu complet: `achizitie_import\test_import_nota_ui.prg` + `.ps1`.
`achizitie_import\test_import_nota_ui.prg` + `.ps1` (wrapper).
**REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al **REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia (ex. "Adauga repere", utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia, NU introducerea
do_adauga_factura prin dialogul real), NU introducerea directa a randurilor in cursoare/tabele directa a randurilor in cursoare/tabele. Popularea directa sare peste validari/filtre/efecte
(ex. Append in rul_temp/introdc). Popularea directa sare peste validari/filtre/efecte laterale laterale si da PASS fals (caz real: un articol "adaugat" direct in cursor trecea testul, dar
si da PASS fals (caz real: articol cont 212 "adaugat" direct in rul_temp trecea testul, dar prin prin fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
dialoguri modale), nu pe pasii fluxului testat. dialoguri modale), nu pe pasii fluxului testat.
## Test UI nou in 5 pasi ## Test UI nou in 5 pasi
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET TALK OFF` (obligatoriu, vezi cap. c); 1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET SAFETY OFF` + `SET TALK OFF` ca PRIMA
`SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE linie (vezi capcana a); `SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
`SET PROCEDURE TO ...\ui_harness.prg ADDITIVE` si (daca ai dialoguri) `ui_harness.prg` si (daca ai dialoguri) `mock_amessagebox.prg`, cu `ADDITIVE`.
`...\mock_amessagebox.prg ADDITIVE`. 2. Creeaza dummy-urile minime (`goExecutor` cu metode reale, `goApp` pt. `ReadIni`, `poAct`
2. Creeaza dummy-urile minime (`goExecutor`, `goApp`, etc.) + cursoarele pe care le cere pt. curs valutar) + cursoarele cerute de Init (RecordSource-urile grilelor).
Init-ul clasei (RecordSource-urile grilelor). 3. Instantiaza clasa, `WindowType=0` (modeless - `Show()` pe modal nu returneaza), `Show()`,
3. Instantiaza clasa, `WindowType=0`, `Show()`, `WindowState=2`. `WindowState=2`.
4. Pe fiecare pas: fa actiunea, verifica efectul in date (loghezi PASS/FAIL cu un helper 4. Pe fiecare pas: fa actiunea, verifica efectul in date (PASS/FAIL cu helper local), apoi
local), apoi `DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la `DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la orchestrator sau
orchestrator sau auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`. auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
5. Ruleaza: `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`. 5. `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
PNG-urile ies in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`. PNG-uri in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
## Capcane / deblocari (toate confirmate in sesiune) ## Capcane / deblocari (toate confirmate in sesiune)
a. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de a. **`SET SAFETY OFF` + `SET TALK OFF` inaintea primului `STRTOFILE`.** Cu SAFETY ON, scrierea
finalul pasilor. Ruleaza-l in background (run_in_background) + asteapta `done.txt`, sau log-ului existent scoate un dialog modal care blocheaza rularea (proces viu, "instanta
timeout mare (300-600s). NU porni VFP separat de orchestrator - si-l lanseaza singur. moarta"). Cu TALK ON, `SUM`/`CALCULATE` isi echo-eaza rezultatele peste formular in
b. **Asteptarea ready_0 prea scurta** omoara VFP exact inainte sa scrie semaforul. Cu `.fxp` screenshots.
cald START apare in ~2s, la rece mult mai mult (zeci de classlib-uri). Foloseste b. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
`-ReadyTimeoutSec` generos (implicit 180s). Retry NUMAI la "start ratat", nu la "start lent". final. Ruleaza-l in background + asteapta `done.txt`, sau timeout 300-600s. NU porni VFP
c. **`SET SAFETY OFF` + `SET TALK OFF` ca PRIMUL lucru, inainte de primul `STRTOFILE`.** separat de orchestrator - si-l lanseaza singur.
Cu SAFETY ON, prima scriere a log-ului (daca fisierul exista deja) scoate dialogul modal c. **Asteptarea `ready_0`**: cu `.fxp` cald START apare in ~2s, la rece mult mai mult (zeci de
"...already exists, overwrite it?" care BLOCHEAZA rularea headless (VFP pare "instanta moarta" classlib-uri). `-ReadyTimeoutSec` generos (implicit 180s); retry NUMAI la "start ratat",
- proces viu, blocat pe dialog). Cu TALK ON, `SUM`/`CALCULATE` (ex. din `recalculeaza`) isi nu la "start lent". Detectia "a pornit" se face pe mtime-ul log-ului > momentul lansarii
echo-eaza rezultatele pe desktop, ca text parazit peste formular in screenshots. Harness-ul (un log stale tinut de un vfp9 zombi pacaleste verificarea "log ne-gol") - omoara zombii
are `HarnessInit`/`SET SAFETY OFF` defensiv in procedurile de scriere, dar testul trebuie inainte de fiecare lansare.
sa faca `SET SAFETY OFF` oricum INAINTE de scrierea lui proprie de "START". d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log gol). Lanseaza
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log ramane gol). `.fxp`-ul PRECOMPILAT. Precompilarea (`COMPILE`+`QUIT`) atarna dupa COMPILE si, rulata in
Lanseaza `.fxp`-ul PRECOMPILAT (`vfp9 -A test.fxp`) - se executa mereu. ACEEASI sesiune powershell, otraveste lansarile ulterioare - ruleaza-o intr-un proces copil
e. **Precompilarea (`COMPILE`+`QUIT`) atarna** dupa COMPILE; rulata in ACEEASI sesiune izolat (`Start-Process -Wait _precompile.ps1`).
powershell care lanseaza apoi testul, otraveste lansarile (nu executa). Ruleaza precompilarea e. **Suitele NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 | Stop-Process
intr-un proces copil izolat (`Start-Process -Wait _precompile.ps1`). -Force` (omoara toate instantele), deci un test lansat in paralel il ucide pe cel in curs.
f. **Detectia "a pornit" pe mtime-ul log-ului > momentul lansarii**, nu pe "log ne-gol": un log f. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` fura focus si se corupe daca
stale tinut deschis de un `vfp9` zombi pacaleste verificarea. Omoara zombii inainte de fiecare utilizatorul lucreaza in paralel - nu se mai foloseste. Capturile se fac cu `PrintWindow`
lansare. (user32, P/Invoke) pe `Process.MainWindowHandle`, flag `2` = `PW_RENDERFULLCONTENT`
g. **Formulare modale** (`WindowType`) - `Show()` nu returneaza si blocheaza testul. Seteaza (fallback `0` daca iese goala); merge cu fereastra acoperita, dar NU minimizata. Fereastra
`goObj.WindowType = 0` (modeless) pentru afisare non-modala automatizata. se muta OFF-SCREEN (`SetWindowPos`, `HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce
h. **Dummy minimali**: `goExecutor` (oExecute/oExecuta creeaza cursorul cerut), `goApp` apare handle-ul si SE RE-IMPINGE la fiecare pas (VFP se reactiveaza singur la
(`ReadIni` pt. font din settings.ini), `poAct`/`oact` (curs valutar). Init-ul recreeaza `Show()`/dialoguri). Consolele powershell copil sunt `-WindowStyle Hidden` (sigur pentru
cursoarele goale din Oracle daca nu-s pre-populate - pre-populeaza-le INAINTE de instantiere. consola, NU pentru GUI VFP - ascunsa, `PrintWindow` poate reda gol). Implementat in
i. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded cu spatii nu gaseste nimic** (`==` e
exact) - foloseste `ALLTRIM(camp) == 'literal'`.
j. **Messagebox-uri de confirmare blocheaza testul headless**. Mock: `FUNCTION amessagebox`
care returneaza direct valoarea butonului (6=Da). Incarca-l cu `SET PROCEDURE ... ADDITIVE`
**PRIMUL** (inaintea `oproceduri_comune.prg`): la nume duplicat de procedura, VFP foloseste
fisierul cautat PRIMUL in lista (verificat empiric: incarcat ultimul PIERDE, primul CASTIGA).
Mock-ul umbreste `amessagebox` si din apelurile din metodele de clasa (ex. `do_sterge_articol`).
k. **Cursor bound la o grila, recreat de Init -> grila pierde coloanele (eroare 1925).** Ex.:
`import_nota.Init` recreeaza `rul_temp` din Oracle daca e gol la instantiere; GridArt ramane
fara coloanele denumite. Fix: pune 1 rand placeholder in cursor INAINTE de instantiere (ca Init
sa nu-l recreeze), apoi **`ZAP`** dupa `Show()` (NU `DELETE ALL`: acesta lasa un rand fizic
sters si pointerul la EOF, iar cod gen `do_calculeaza_diferente` face `GO recno_EOF` -> eroare 5).
l. **CREATEOBJECT/ADDPROPERTY se comporta gresit apelate DINTR-O PROCEDURA in acest runtime headless**
(obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din programul PRINCIPAL
merg. Construieste obiectele (ex. `toDlg` pt. `do_adauga_factura`) INLINE in main, nu intr-un
helper. (Cauza exacta neclara; reproductibil.)
m. **`gl406=.T.` (SAFT) activeaza cod care are nevoie de mock-uri suplimentare**, altfel crapa sau
taxcode ramane mereu NULL: `GetTaxCode`/`GetTaxCodeIdPart` (oproceduri_comune.prg) apeleaza
`update_jtva_coloane()` care ruleaza `goExecutor.oExecute("select * from vjtva_coloane ...", <cursor>)`
- mock-ul de `oExecute` trebuie sa intercepteze `'VJTVA_COLOANE' $ UPPER(tcSql)` si sa returneze
un cursor cu structura reala a view-ului (`id_jtva_coloana, denumire, jc, jv, coloana_jc,
coloana_jv, cota_tva, id_tva, taxcode, taxcode_neexigibil, taxcode_n50, taxcode_n50_tvai,
taxcode_n100, taxcode_regularizare, taxcode_tvai, afisat, ordine`) - codul apelant face singur
`INDEX ON id_jtva_coloana TAG id_jtva` dupa, nu trebuie indexat in mock. Cand `id_partc`/`id_partd`
pe rand nu e gol, `GetTaxCodeIdPart` cauta si codul fiscal al partenerului prin
`goExecutor.oSelect2Value(sql, @toValue)` (`GetCodFiscalPartenerById`) - metoda lipseste din
dummyexecutor-ul de baza, trebuie adaugata (returneaza cod fiscal gol -> `VERIFICA_RTVAI` sare
peste apelul Oracle real de validare CIF).
n. **Explicatia TVA de pe randul T ("in tandem" cu baza) NU se scrie prin cod separat** - e pur si
simplu o A DOUA intrare in `jtva_coloane2`/`vjtva_coloane` (coloana `id_tva` de pe randul bazei
punctand spre `id_jtva_coloana` al randului TVA); `aplica_explicatie_tva` (ointroduceri.prg
~9807) citeste acea a doua intrare si o scrie pe rand T. Populeaza mock-ul cu ambele randuri
(baza + TVA), nu doar cu unul si nu concatena manual "TVA " + denumirea bazei in test.
o. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` (captura ecranului intreg) fura
focus si se corupe daca utilizatorul lucreaza in paralel - NU se mai foloseste. Capturile se fac
cu `PrintWindow` (user32, P/Invoke) pe HANDLE-ul ferestrei vfp9 (`Process.MainWindowHandle`),
flag `2` = `PW_RENDERFULLCONTENT` (fallback flag `0` daca iese goala); merge cu fereastra
acoperita/in fundal, dar NU minimizata. Fereastra e mutata OFF-SCREEN (`SetWindowPos` cu
`HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce apare handle-ul si RE-IMPINSA la fiecare
pas (VFP isi reactiveaza singur fereastra la `Show()`/dialoguri modale - o singura impingere la
lansare nu e suficienta). Toate consolele powershell copil (precompilare) sunt `-WindowStyle
Hidden` (sigur pentru consola; NU si pentru GUI VFP, care trebuie doar off-screen, nu ascuns -
PrintWindow pe fereastra minimizata/ascunsa poate reda gol). Implementat in
`vfp_ui_harness.ps1` (Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si `vfp_ui_harness.ps1` (Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si
`_precompile.ps1` (acelasi Push-Offscreen pe fereastra tranzitorie de compilare). `_precompile.ps1`.
p. **Cursorul `introdc` FARA indexul `ord_doc` schimba documentul "principal" la sincronizari g. **Mock de messagebox**: `FUNCTION amessagebox` care returneaza direct valoarea butonului
repetate** (sincronizeaza() apelat de mai multe ori, ex. la adaugarea a 2+ articole succesiv (6=Da), incarcat cu `SET PROCEDURE ... ADDITIVE` **PRIMUL**, inaintea fisierelor aplicatiei:
prin fluxul real): identificarea documentului principal (`Go Top` + `Locate For la nume duplicat de procedura VFP foloseste fisierul cautat PRIMUL (verificat empiric).
Inlist(tip_rand,'B','S') And !Empty(doc_key)`) e stabila in productie doar pentru ca Astfel umbreste `amessagebox` si in apelurile din metodele de clasa.
`achizitie_import` (COMUN\programe\ointroduceri.prg:1616-1618) creeaza, INAINTE de h. **Cursorii ceruti de Init**: pre-populeaza-i INAINTE de instantiere (Init-ul recreeaza din
`CREATEOBJECT('import_nota')`, indexul `ord_doc` (`Str(Nvl(nr_doc,0),8) + Oracle cursoarele goale, iar recrearea lasa grid-ul fara coloane, eroare 1925). Pune 1 rand
Icase(tip_rand='B','1', tip_rand='S','2', tip_rand='T','3', tip_rand='D','4', '5') + placeholder inainte, apoi **`ZAP`** dupa `Show()` — NU `DELETE ALL`, care lasa un rand fizic
Padr(Nvl(scd,''),10)`) + `SET ORDER TO ord_doc`, care ordoneaza dupa `nr_doc` (S-urile sters si pointerul la EOF (cod care face `GO <recno>` da eroare 5, vezi
principalei mostenesc nr_doc-ul ei) - documentul cu `nr_doc` minim ramane mereu acelasi. Un `conventie_go_recno.md`).
test care creeaza `introdc` fara acest index ramane pe ordinea FIZICA (recno): dupa ce i. **Cursorii trebuie sa aiba EXACT indecsii creati de fluxul real** inainte de instantiere
principala e sparta (randul B sters, randurile S adaugate la coada), urmatorul apel de (fluxul real ii creeaza in metoda apelanta, ex. un `INDEX ON ... TAG ord_doc` +
sincronizeaza() gaseste gresit ca "principal" documentul cu cel mai mic recno ramas si il `SET ORDER TO` inainte de `CREATEOBJECT`). Fara ei cursorul ramane pe ordinea FIZICA, iar
sparge si pe el - cascada se repeta la fiecare apel ulterior. Fix: dupa popularea `introdc`, logica de tip `GO TOP` + `LOCATE FOR ...` alege alt rand decat in productie - rezultat fals,
INAINTE de `CREATEOBJECT('import_nota', ...)`, adauga exact acelasi `INDEX ON ... TAG ord_doc` uneori in cascada la apeluri repetate. Verifica in codul apelant ce indecsi se creeaza si
+ `SET ORDER TO ord_doc` ca in `achizitie_import`. replica-i identic.
q. **Suitele de teste NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 | j. **Mock-urile de date trebuie sa reproduca structura si semantica REALA a sursei** (view
Stop-Process -Force` (omoara TOATE instantele vfp9 de pe masina), deci un test lansat in Oracle: toate coloanele; relatii intre randuri: toate randurile implicate, nu unul singur cu
paralel ucide testul aflat in curs si corupe rezultatele. Ruleaza suitele strict secvential. valori "compuse" manual in test). Un mock incomplet lasa campuri mereu NULL sau ascunde
r. **Fara SELECT-SQL pe cursorul legat de grid**: SELECT ... FROM introdc INTO CURSOR intr-un pasul care se testeaza. Cand un flag global activeaza cod suplimentar (ex. `gl406`), acel
test, cat timp introdc e RecordSource-ul unui grid viu, poate omori procesul vfp9 silentios cod cere mock-uri in plus - verifica ce apeleaza si adauga metodele lipsa din dummy.
(fara eroare catchabila, fara semafor). Foloseste xBase nativ (COUNT FOR ... TO, k. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded nu gaseste nimic** (`==` e exact) -
CALCULATE, LOCATE) care nu comuta zona de lucru a grid-ului. foloseste `ALLTRIM(camp) == 'literal'`.
l. **`CREATEOBJECT`/`ADDPROPERTY` se comporta gresit apelate DINTR-O PROCEDURA in acest runtime
headless** (obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din
programul PRINCIPAL merg. Construieste obiectele-parametru INLINE in main, nu intr-un helper.
(Cauza neclara; reproductibil.)
m. **Fara `SELECT-SQL` pe cursorul legat de grid**: `SELECT ... FROM <cursor> INTO CURSOR` cat
timp cursorul e RecordSource-ul unui grid viu poate omori procesul vfp9 silentios (fara
eroare catchabila, fara semafor). Foloseste xBase nativ (`COUNT FOR ... TO`, `CALCULATE`,
`LOCATE`), care nu comuta zona de lucru a grid-ului.