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:
@@ -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).
|
|
||||||
|
|
||||||
## Fisiere de testare (foloseste-le la orice depanare/testare viitoare)
|
|
||||||
|
|
||||||
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
|
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
|
||||||
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO).
|
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO, parola
|
||||||
Echivalentul headless al `COMUN\programe\test_init_env.prg` (care ramane varianta
|
schemei `ROMFASTSOFT`; user aplicatie "MARIUS M" / "123"). Seteaza PATH/CLASSLIB/PROCEDURE
|
||||||
interactiva, cu INPUTBOX si meniu de lansare formulare).
|
ca programul principal si instantiaza goConn/goExecutor/goApp/goLog/goFirma/goCalendar +
|
||||||
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - exemplu complet de harness:
|
pachetele de sesiune Oracle. Dupa apel poti face `CREATEOBJECT('frm_xxx')` si apela metodele
|
||||||
init mediu -> replicare flux (cursoarele din modificanote) -> instantiere forma reala ->
|
punctual. Varianta interactiva (INPUTBOX + meniu): `COMUN\programe\test_init_env.prg`.
|
||||||
executie pas cu pas cu logare -> verdict OK/BUG. Copiaza-i structura pentru bug-uri noi.
|
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - harness model: init mediu -> replicare
|
||||||
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness instantiere import_nota
|
flux -> instantiere forma reala -> executie pas cu pas cu logare -> verdict OK/BUG.
|
||||||
(ROAGEST) fara Oracle: cursori dummy + goExecutor dummy; parametrul LPARAMETERS
|
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness fara Oracle (cursori + goExecutor
|
||||||
incarca o copie de clasa din scratchpad in loc de cea reala (bisectie A/B).
|
dummy) cu parametru care incarca o copie de clasa din scratchpad, pentru bisectie A/B.
|
||||||
- `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
|
|
||||||
`ROMFASTSOFT`; utilizator de aplicatie "MARIUS M" / parola "123" (unde e nevoie de login).
|
|
||||||
|
|
||||||
## 1. Citirea log-ului aplicatiei (log.txt in radacina proiectului)
|
## 2. Rulare headless
|
||||||
|
|
||||||
`ErrorHandler` (roacont.prg) scrie pentru fiecare eroare: `nErrror`, `cMethod`,
|
|
||||||
`line` (0 daca exe-ul e compilat fara debug info) si stack-ul complet.
|
|
||||||
|
|
||||||
Semantica ON ERROR (esentiala la interpretare):
|
|
||||||
- Dupa ce handlerul face RETURN, executia CONTINUA de la linia urmatoare din metoda
|
|
||||||
care a crapat. O singura cauza produce deci CASCADE de erori.
|
|
||||||
- Tipar clasic: eroare pe linia `WITH <expr>` => urmatoarele linii `.prop = ...` dau
|
|
||||||
1940 "Expression is not valid outside of WITH/ENDWITH", apoi `ENDWITH` da
|
|
||||||
1939 "WITH/ENDWITH mismatch". PRIMA eroare din secventa e cea reala; restul e zgomot.
|
|
||||||
- Erorile din interiorul TRY/CATCH nu ajung in log (inghitite). Un CATCH gol poate
|
|
||||||
ascunde ani de zile un defect latent, care "explodeaza" cand codul din jur se repara
|
|
||||||
(exact cazul typo-ului oMinHeaderHeight din cusfonthandler).
|
|
||||||
- In stack, liniile "ON..." sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
|
|
||||||
|
|
||||||
## 2. Rulare headless a scripturilor VFP
|
|
||||||
|
|
||||||
Sablonul unui script de diagnostic (vezi `utile\Teste\test_repro_cnrcrt_footer.prg`):
|
|
||||||
- logare in fisier cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
|
|
||||||
- `ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza
|
|
||||||
si continua - mimeaza comportamentul ErrorHandler din aplicatie;
|
|
||||||
- `QUIT` obligatoriu la final (altfel ramane vfp9.exe agatat);
|
|
||||||
- lansare 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, `-T` fara ecran de start; parametrul dupa .prg ajunge in LPARAMETERS.
|
|
||||||
- LINENO() din handler e relativ la inceputul metodei (utile pentru localizare in .vc2:
|
|
||||||
linia din metoda = linia raportata + linia PROCEDURE din .vc2).
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 3. Mediu complet cu Oracle, fara interactiune
|
|
||||||
|
|
||||||
`utile\Teste\test_init_env_auto.prg` = varianta neinteractiva a
|
|
||||||
`programe\test_init_env.prg` (fara INPUTBOX/xmenu): primeste host/schema/parola ca
|
|
||||||
parametri, seteaza PATH/CLASSLIB/PROCEDURE ca roacont.prg, instantiaza
|
|
||||||
goConn/goExecutor/goApp/goLog, incarca goFirma/goCalendar/optiuni si pachetele de
|
|
||||||
sesiune Oracle. Dupa apel poti instantia forme reale (`CREATEOBJECT('frm_xxx', ...)`)
|
|
||||||
si apela metodele lor punctual, cu cursoarele pregatite ca in fluxul real.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 4. Bisectia versiunilor de clase (regresii)
|
|
||||||
|
|
||||||
Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge SVN:
|
|
||||||
1. `git show <rev_vechi>:clase/fisier.vcx > temp\fisier.vcx` (+ .vct identic);
|
|
||||||
2. convertit la text pentru diff: `vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`;
|
|
||||||
3. in scriptul de test, `SET CLASSLIB TO temp\fisier.vcx ADDITIVE` (varianta veche)
|
|
||||||
vs. cea din COMUN (varianta noua) - A/B pe acelasi flux;
|
|
||||||
4. cand difera rezultatul, cauza e in diff-ul acelei clase (sau intr-un defect latent
|
|
||||||
pe care noul cod il activeaza - vezi capcanele de mai jos).
|
|
||||||
|
|
||||||
Varianta pe backup-uri de runda (fara git): copiaza `*.vc2.pre_rundaN.bak` din cache
|
|
||||||
intr-un folder temp ca `ointroduceri.vc2`, regenereaza binarul direct cu
|
|
||||||
`FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune `..\include\foxpro.h` langa,
|
|
||||||
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).
|
|
||||||
Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource ->
|
|
||||||
camp simplu, sterge linii de proprietate din mijlocul blocului) - NU sterge
|
|
||||||
obiecte/coloane din ADD OBJECT (strica structura: "Cannot add this object to a Grid").
|
|
||||||
|
|
||||||
## 5. Capcane VFP descoperite (de verificat la bug-uri asemanatoare)
|
|
||||||
|
|
||||||
- `SELECT ... INTO CURSOR` NU mosteneste indecsii cursorului sursa. Orice
|
|
||||||
`Seek(..., 'cursor', 'tag')` pe copie crapa. `update_jtva_coloane` creeaza
|
|
||||||
`INDEX ON id_jtva_coloana TAG id_jtva`; daca ii copiezi rezultatul, recreeaza indexul.
|
|
||||||
- Un grid isi pierde TOATE coloanele (ColumnCount devine 0, fara nicio eroare in log)
|
|
||||||
daca un ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse
|
|
||||||
programatic (ex. FontName/FontSize per coloana in cusfonthandler.ApplyFontToGrid,
|
|
||||||
la Init-ul formei prin _frmbase.SetFont). Simptome tipice in aval:
|
|
||||||
`_grdfooter.attachtogrid` copiaza 0 coloane, apoi `calctotal` da
|
|
||||||
"Property <cColWithLabel> is not found" la `Getpem(This, This.cColWithLabel)`.
|
|
||||||
- ControlSource-uri cu campuri inexistente in RecordSource sunt tolerate la afisare,
|
|
||||||
dar orice expresie care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie
|
|
||||||
nedefinita) declanseaza comportamentul de mai sus.
|
|
||||||
- Fix-ul unui typo intr-o clasa partajata poate activa defecte latente in aval
|
|
||||||
(codul care nu rulase niciodata incepe sa ruleze). La regresii dupa fix-uri
|
|
||||||
"inofensive" in COMUN, cauta defectul latent, nu doar diff-ul.
|
|
||||||
- **Literal string de peste 255 de caractere = eroare de COMPILARE, nu de runtime** (masurat
|
|
||||||
24.07.2026: 255 trece, 256 pica). Mesajul difera dupa forma liniei: `Unrecognized command
|
|
||||||
verb` daca linia incepe cu un apel de metoda (`goExecutor.oExecuta("<sql lung>", 'c')`),
|
|
||||||
`Command contains unrecognized phrase/keyword` la o atribuire (`lc = "<sir lung>"`).
|
|
||||||
Insidios: restul programului ruleaza normal, doar linia aceea lipseste din `.fxp` si da
|
|
||||||
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`
|
|
||||||
... `ENDTEXT` sau concatenat din bucati, nu ca literal unic. Verificare rapida:
|
|
||||||
`awk 'length($0) > 260 {print NR": "length($0)}' fisier.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). Caz: import_nota.GridArt
|
|
||||||
Column7/8/9 cu `round(...)` (ROAGEST 16.07.2026); model corect: Grid1.cTaxCode
|
|
||||||
(`Bound = .F.` + expresie, functional de ani).
|
|
||||||
|
|
||||||
## 6. Capcane la SCRIEREA scriptului de test (nu ale bug-ului investigat)
|
|
||||||
|
|
||||||
Descoperite depanand harness-ul pentru "Data type mismatch" la `CREATEOBJECT('IMPORT_nota',...)`
|
|
||||||
(ROAGEST, 16.07.2026) - de verificat oricand un script de test nou pare "agatat".
|
|
||||||
|
|
||||||
- **`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):
|
|
||||||
|
|
||||||
|
`vfp9.exe -c<config.fpw> -A -T "<script.prg>" <param>`, lansat din PowerShell cu timeout:
|
||||||
```
|
```
|
||||||
SET SAFETY OFF
|
$p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru
|
||||||
lcDir = '<scratchpad>\'
|
$p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() }
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
- `-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`.
|
||||||
|
|
||||||
Nu inlocuieste testul functional: `COMPILE` valideaza sintaxa, nu apeluri catre metode
|
## 3. Citirea log-ului aplicatiei (log.txt din radacina proiectului)
|
||||||
inexistente sau proprietati gresite (acelea apar abia la runtime).
|
|
||||||
|
|
||||||
## 8. Studiu de caz: "Property CNRCRT is not found" (15.07.2026)
|
`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.
|
||||||
|
|
||||||
Lant complet: `frm_import_extrase_banca.modificanote` copia `crsJTVATemp` fara index
|
## 4. Verificare de sintaxa prin compilare headless (dupa orice editare de `.prg`)
|
||||||
(defect latent) -> coloana `cExplicatieTVA` din `frm_modific2024.grdRulaje` face
|
|
||||||
`Seek(trul.id_jtva_coloana,'crsJtvaTemp','id_jtva')` in ControlSource -> fix-ul
|
Cel mai ieftin test dupa o modificare: `COMPILE` headless, fara aplicatie si fara Oracle.
|
||||||
typo-ului `oMinHeaderHeight`->`nMinHeaderHeight` in `cusfonthandler` (r17869) a facut
|
- Compileaza o COPIE a fisierului in scratchpad, nu sursa din working copy (`COMPILE` scrie
|
||||||
ca ApplyFontToGrid sa atinga coloanele la Init -> grid-ul a ramas cu 0 coloane ->
|
`.fxp` langa `.prg`, care ar aparea ca modificare in SVN). VFP nu rezolva simboluri externe
|
||||||
footer-ul a crapat in calctotal. Fix: `INDEX ON id_jtva_coloana TAG id_jtva` dupa
|
la compilare, deci copia izolata e suficienta pentru sintaxa.
|
||||||
SELECT INTO in modificanote. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`
|
- Sterge `.err`-ul vechi INAINTE de `COMPILE`; verdictul = existenta lui `<nume>.err` dupa
|
||||||
(mod FARAINDEX reproduce bug-ul, mod CUINDEX valideaza fix-ul).
|
(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
|
||||||
|
|
||||||
|
- **`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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user