From b82acf06ce8ce4c8a30ac8f5e0de49d812623179 Mon Sep 17 00:00:00 2001 From: squid-protocol Date: Wed, 7 Oct 2026 15:55:10 -0400 Subject: [PATCH 1/2] equivalence: CICS tasks with no COMMAREA and a per-scenario COMMAREA length (#4270) - "commarea": null: a program that takes no COMMAREA (EIBCALEN 0 on both sides, no DTO; the det service's handleLink() is the java-facade entry). A missing or empty commarea is refused by name (was KeyError / IndexError), as is a COMMAREA in a scenario or an event of such a case. - "commarea_length": N (oracle_assumptions.md X23): the program is given exactly N bytes. The stub's GGCAREA puts them before a guard page; the det port cuts DFHCOMMAREA's storage to N (CicsTask.withExactCommarea). A reference past EIBCALEN is refused on both sides, and the task is judged up to it, as X6 judges a WRITEQ past FROM. A judged task now passes only when both sides stopped at the same refusal. - GenApp: short-COMMAREA scenarios for LGACDB01, LGACDB02, LGDPDB01, LGIPDB01. - New cases for the async credit-card estate: asynccc-getpol, asynccc-getspnd, asynccc-updcsdb. - The generated test imports ScreenModel only in an estate with BMS maps. - Coverage ledger and the GenApp evidence report refreshed. Co-Authored-By: Claude Opus 5.5 --- .../evidence_report/cics-genapp/report.json | 32 +-- .../evidence_report/cics-genapp/report.md | 32 +-- docs/language_status/oracle_assumptions.md | 42 +++- .../cobol_to_java_transaction_forge.py | 14 ++ .../cobol_to_java/det/cobolrt/Storage.java | 4 +- gitgalaxy/tools/cobol_to_java/det/program.py | 48 ++++- tests/cobol_mainframe/test_det_translate.py | 33 +++ .../cobol_mainframe/test_equivalence_cics.py | 121 +++++++++++ tests/equivalence/asynccc-getpol/LICENSE | 201 ++++++++++++++++++ tests/equivalence/asynccc-getpol/NOTICE | 8 + tests/equivalence/asynccc-getpol/case.json | 21 ++ tests/equivalence/asynccc-getspnd/LICENSE | 201 ++++++++++++++++++ tests/equivalence/asynccc-getspnd/NOTICE | 8 + tests/equivalence/asynccc-getspnd/case.json | 21 ++ tests/equivalence/asynccc-updcsdb/LICENSE | 201 ++++++++++++++++++ tests/equivalence/asynccc-updcsdb/NOTICE | 8 + tests/equivalence/asynccc-updcsdb/case.json | 21 ++ tests/equivalence/cics/ggcics.c | 49 +++++ tests/equivalence/det_sweep_coverage.json | 97 +++++++-- tests/equivalence/genapp-lgacdb01/case.json | 15 +- tests/equivalence/genapp-lgacdb02/case.json | 22 +- tests/equivalence/genapp-lgdpdb01/case.json | 28 +++ tests/equivalence/genapp-lgipdb01/case.json | 66 ++++++ tests/equivalence/genapp-lgucdb01/case.json | 2 +- tests/tools/equivalence_cics.py | 125 +++++++++-- tests/tools/evidence.py | 14 +- 26 files changed, 1343 insertions(+), 91 deletions(-) create mode 100644 tests/equivalence/asynccc-getpol/LICENSE create mode 100644 tests/equivalence/asynccc-getpol/NOTICE create mode 100644 tests/equivalence/asynccc-getpol/case.json create mode 100644 tests/equivalence/asynccc-getspnd/LICENSE create mode 100644 tests/equivalence/asynccc-getspnd/NOTICE create mode 100644 tests/equivalence/asynccc-getspnd/case.json create mode 100644 tests/equivalence/asynccc-updcsdb/LICENSE create mode 100644 tests/equivalence/asynccc-updcsdb/NOTICE create mode 100644 tests/equivalence/asynccc-updcsdb/case.json diff --git a/docs/language_status/evidence_report/cics-genapp/report.json b/docs/language_status/evidence_report/cics-genapp/report.json index e8bf134443..92c8f1fa8a 100644 --- a/docs/language_status/evidence_report/cics-genapp/report.json +++ b/docs/language_status/evidence_report/cics-genapp/report.json @@ -1349,7 +1349,7 @@ "cases": [ { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", @@ -1932,7 +1932,7 @@ "cases": [ { "case": "genapp-lgdpdb01", - "case_scenarios": 9, + "case_scenarios": 11, "db2": true, "det": { "state": "not run", @@ -2799,7 +2799,7 @@ "cases": [ { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", @@ -2813,7 +2813,7 @@ ], "chosen": { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", @@ -3055,7 +3055,7 @@ "cases": [ { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", @@ -3068,7 +3068,7 @@ }, { "case": "genapp-lgacdb02", - "case_scenarios": 9, + "case_scenarios": 10, "db2": true, "det": { "state": "not run", @@ -3082,7 +3082,7 @@ ], "chosen": { "case": "genapp-lgacdb02", - "case_scenarios": 9, + "case_scenarios": 10, "db2": true, "det": { "state": "not run", @@ -4004,7 +4004,7 @@ "cases": [ { "case": "genapp-lgdpdb01", - "case_scenarios": 9, + "case_scenarios": 11, "db2": true, "det": { "state": "not run", @@ -4018,7 +4018,7 @@ ], "chosen": { "case": "genapp-lgdpdb01", - "case_scenarios": 9, + "case_scenarios": 11, "db2": true, "det": { "state": "not run", @@ -5024,7 +5024,7 @@ "cases": [ { "case": "genapp-lgipdb01", - "case_scenarios": 40, + "case_scenarios": 46, "db2": true, "det": { "state": "not run", @@ -5038,7 +5038,7 @@ ], "chosen": { "case": "genapp-lgipdb01", - "case_scenarios": 40, + "case_scenarios": 46, "db2": true, "det": { "state": "not run", @@ -5514,7 +5514,7 @@ "cases": [ { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", @@ -5527,7 +5527,7 @@ }, { "case": "genapp-lgacdb02", - "case_scenarios": 9, + "case_scenarios": 10, "db2": true, "det": { "state": "not run", @@ -5540,7 +5540,7 @@ }, { "case": "genapp-lgdpdb01", - "case_scenarios": 9, + "case_scenarios": 11, "db2": true, "det": { "state": "not run", @@ -5553,7 +5553,7 @@ }, { "case": "genapp-lgipdb01", - "case_scenarios": 40, + "case_scenarios": 46, "db2": true, "det": { "state": "not run", @@ -5580,7 +5580,7 @@ ], "chosen": { "case": "genapp-lgacdb01", - "case_scenarios": 5, + "case_scenarios": 6, "db2": true, "det": { "state": "not run", diff --git a/docs/language_status/evidence_report/cics-genapp/report.md b/docs/language_status/evidence_report/cics-genapp/report.md index 41ce623391..a1c8bbb248 100644 --- a/docs/language_status/evidence_report/cics-genapp/report.md +++ b/docs/language_status/evidence_report/cics-genapp/report.md @@ -54,18 +54,18 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 | base/src/lgdpvs01.cbl | L2 | 28/28 | 0 | genapp-lgdpvs01 | equal | 5 | none | not measured | not measured | not measured | not yet measured (#4628) | 2/5 | | base/src/lgucvs01.cbl | L2 | 32/32 | 0 | genapp-lgucvs01 | equal | 5 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/7 | | base/src/lgupvs01.cbl | L2 | 55/55 | 0 | genapp-lgupvs01 | equal | 6 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/7 | -| base/src/lgacdb01.cbl | L1 | 61/61 | 0 | genapp-lgacdb01 | not run | 5 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/6 | -| base/src/lgacdb02.cbl | L1 | 39/39 | 0 | genapp-lgacdb02 | not run | 9 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | +| base/src/lgacdb01.cbl | L1 | 61/61 | 0 | genapp-lgacdb01 | not run | 6 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/6 | +| base/src/lgacdb02.cbl | L1 | 39/39 | 0 | genapp-lgacdb02 | not run | 10 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgacus01.cbl | L1 | 34/34 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgapdb01.cbl | L1 | 124/124 | 0 | genapp-lgapdb01 | not run | 4 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgapol01.cbl | L1 | 30/30 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | -| base/src/lgdpdb01.cbl | L1 | 45/45 | 0 | genapp-lgdpdb01 | not run | 9 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | +| base/src/lgdpdb01.cbl | L1 | 45/45 | 0 | genapp-lgdpdb01 | not run | 11 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgdpol01.cbl | L1 | 36/36 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgicdb01.cbl | L1 | 44/44 | 0 | genapp-lgicdb01 | not run | 12 | stale | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgicus01.cbl | L1 | 34/34 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | -| base/src/lgipdb01.cbl | L1 | 238/238 | 0 | genapp-lgipdb01 | not run | 40 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | +| base/src/lgipdb01.cbl | L1 | 238/238 | 0 | genapp-lgipdb01 | not run | 46 | none | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | | base/src/lgipol01.cbl | L1 | 26/26 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 3/5 | -| base/src/lgstsq.cbl | L1 | 26/26 | 0 | genapp-lgacdb01 | not run | 5 | none | not measured | not measured | not measured | not yet measured (#4628) | 5/6 | +| base/src/lgstsq.cbl | L1 | 26/26 | 0 | genapp-lgacdb01 | not run | 6 | none | not measured | not measured | not measured | not yet measured (#4628) | 5/6 | | base/src/lgtestp1.cbl | L1 | 152/152 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 7/8 | | base/src/lgtestp2.cbl | L1 | 140/140 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 7/8 | | base/src/lgtestp3.cbl | L1 | 137/137 | 0 | — | — | — | — | not measured | not measured | not measured | not yet measured (#4628) | 7/8 | @@ -89,7 +89,7 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** paragraph coverage >= 100.0 (coverage not measured: no fresh det-sweep ledger entry and no local sweep) - **Translation:** 25/25 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgacdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (5 scenarios in case.json) + - evidence record: none (6 scenarios in case.json) - **Executed equivalence** (genapp-lgacvs01, program, the case this report judges): det port equal (CI det-sweep ratchet on main) - evidence record: none (4 scenarios in case.json) - **Coverage:** not measured @@ -138,7 +138,7 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** paragraph coverage >= 100.0 (coverage not measured: no fresh det-sweep ledger entry and no local sweep) - **Translation:** 28/28 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgdpdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (9 scenarios in case.json) + - evidence record: none (11 scenarios in case.json) - **Executed equivalence** (genapp-lgdpvs01, program, the case this report judges): det port equal (CI det-sweep ratchet on main) - evidence record: none (5 scenarios in case.json) - **Coverage:** not measured @@ -213,7 +213,7 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** its det port equal on every scenario of genapp-lgacdb01: a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given - **Translation:** 61/61 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgacdb01, program, Db2, the case this report judges): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (5 scenarios in case.json) + - evidence record: none (6 scenarios in case.json) - **Coverage:** not measured - **Oracle backing** (a column, not a level; per CICS command; DIFFERS assumptions reached: not measured): @@ -236,9 +236,9 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** its det port equal on every scenario of genapp-lgacdb02: a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given - **Translation:** 39/39 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgacdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (5 scenarios in case.json) + - evidence record: none (6 scenarios in case.json) - **Executed equivalence** (genapp-lgacdb02, program, Db2, the case this report judges): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (9 scenarios in case.json) + - evidence record: none (10 scenarios in case.json) - **Coverage:** not measured - **Oracle backing** (a column, not a level; per CICS command; DIFFERS assumptions reached: not measured): @@ -324,7 +324,7 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** its det port equal on every scenario of genapp-lgdpdb01: a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given - **Translation:** 45/45 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgdpdb01, program, Db2, the case this report judges): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (9 scenarios in case.json) + - evidence record: none (11 scenarios in case.json) - **Coverage:** not measured - **Oracle backing** (a column, not a level; per CICS command; DIFFERS assumptions reached: not measured): @@ -410,7 +410,7 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** its det port equal on every scenario of genapp-lgipdb01: a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given - **Translation:** 238/238 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgipdb01, program, Db2, the case this report judges): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (40 scenarios in case.json) + - evidence record: none (46 scenarios in case.json) - **Coverage:** not measured - **Oracle backing** (a column, not a level; per CICS command; DIFFERS assumptions reached: not measured): @@ -453,13 +453,13 @@ Translation measured by `cics_census.py survey` at translator commit `3bcbb8c623 - **Next level needs:** its det port equal on every scenario of genapp-lgacdb01: a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given - **Translation:** 26/26 statements, 0 holes; whole: yes - **Executed equivalence** (genapp-lgacdb01, linked, Db2, the case this report judges): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (5 scenarios in case.json) + - evidence record: none (6 scenarios in case.json) - **Executed equivalence** (genapp-lgacdb02, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (9 scenarios in case.json) + - evidence record: none (10 scenarios in case.json) - **Executed equivalence** (genapp-lgdpdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (9 scenarios in case.json) + - evidence record: none (11 scenarios in case.json) - **Executed equivalence** (genapp-lgipdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - - evidence record: none (40 scenarios in case.json) + - evidence record: none (46 scenarios in case.json) - **Executed equivalence** (genapp-lgucdb01, linked, Db2): det port not run (a Db2 case: CI's det-sweep skips Db2 cases and no local sweep was given) - evidence record: none (6 scenarios in case.json) - **Coverage:** not measured diff --git a/docs/language_status/oracle_assumptions.md b/docs/language_status/oracle_assumptions.md index b80bbaf37a..d13c09bc68 100644 --- a/docs/language_status/oracle_assumptions.md +++ b/docs/language_status/oracle_assumptions.md @@ -99,6 +99,7 @@ verdict, but it limits what the proof says about inputs outside the scenarios. | X19 | CICS | ASSIGN on the det port: STARTCODE (TD / S / SD), USERID (the default user), FACILITY / SCRNHT / SCRNWD (INVREQ RESP2 5 without a terminal) from facts the harness states; no data area written when ASSIGN raises INVREQ; OPID, NETNAME, TERMCODE, FCI, the other screen sizes, work-area lengths and the rest refused | ASSUMED (REFUSED where the harness cannot decide) | yes (cics-crucible gt-assign-startcode, unreleased) | | X20 | CICS | SEND TEXT on the det port and the stub: TERMINAL accepted as the default output disposition (the principal facility; the event is that of SEND TEXT without it); ACCUM, PAGING, SET, REQID, HEADER, TRAILER, JUSTIFY / JUSFIRST / JUSLAST, the printer, partition and LDC options, MSR, FMHPARM, DEFAULT / ALTERNATE refused | MATCHED (REFUSED where the region cannot decide) | yes (cics-crucible gt-send-text-terminal, unreleased) | | X21 | CICS | EIBTASKN: the task's number is a stated fact of the run (`$GGCICS_TASKN` / `CicsTask.withTaskNumber`, a case's or scenario's `"taskn"`, default 0), not the number CICS assigns; a value outside 0 to 9,999,999 refused | DIFFERS (the value) / MATCHED (both sides) | yes (every CICS task; read by CBSA's Db2 programs, GenApp LGICDB01) | +| X23 | CICS | A COMMAREA of a stated length (a scenario's `commarea_length`, EIBCALEN shorter than the record): the program is given exactly those bytes; a reference past EIBCALEN refused on both sides, the task judged up to it | ASSUMED (REFUSED past EIBCALEN) | yes (GenApp LGACDB01, LGACDB02, LGDPDB01, LGIPDB01) | | L1 | LE | CEEDAYS: documented pictures only | MATCHED / REFUSED | yes | | L2 | LE | CEE3ABD abend codes | MATCHED | yes | | L3 | LE | WORKING-STORAGE with no VALUE clause: GnuCOBOL's spaces vs LE's STORAGE option on z/OS | ASSUMED | yes (CardDemo READACCT OUTFILE, 2 bytes) | @@ -545,8 +546,9 @@ The length READQ TS returns on ITEMERR or QIDERR is not documented, so it is not - **#4173.** An SQL-fault task (M2) that reaches a LINK to a program the case does not run is judged up to and including that LINK -- its events and the COMMAREA's bytes as LINKed, byte for byte. With LGSTSQ in a case's `"programs"` (GenApp's Db2 cases, #4607) the task runs on to the X6 refusal instead. -- **Still out of reach:** WRITE-ERROR-MESSAGE's `IF EIBCALEN < 91` true side needs a COMMAREA shorter than 91 bytes; the - harness passes each case's whole COMMAREA (a per-scenario length is a follow-up). +- **Reached since (#4270, X23):** WRITE-ERROR-MESSAGE's `IF EIBCALEN < 91` true side in LGACDB02, LGDPDB01 and + LGIPDB01, by scenarios that state a COMMAREA shorter than 91 bytes (`commarea_length`). LGACDB01's and LGUCDB01's + need a failing statement whose host variables lie past EIBCALEN (X23). ### X7. ASSIGN INVOKINGPROG / PROGRAM, and several programs in one task — MATCHED - ASSIGN PROGRAM is the running program, INVOKINGPROG the program that LINKed or XCTLed to it (blanks for a task's @@ -850,6 +852,42 @@ The length READQ TS returns on ITEMERR or QIDERR is not documented, so it is not `tests/cics_crucible/test_cics_runtimes.py` (the stub and CicsTask, stated, unstated and refused) and `tests/cobol_mainframe/test_equivalence_cics.py` (the drivers, the scenario / case / default order). +### X23. A COMMAREA of a stated length — ASSUMED, a reference past EIBCALEN REFUSED (#4270) +- **What IBM documents** (CICS TS 6.x, LINK: "COMMAREA(data-area) specifies a communication area that is to be made + available to the invoked program ... LENGTH(data-value) specifies the length (halfword binary value) in bytes of the + COMMAREA"; the invoked program's EIBCALEN is that length). A caller may pass fewer bytes than the invoked program's + DFHCOMMAREA declares, and GenApp's services test for it: `IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN` -> '98', and + WRITE-ERROR-MESSAGE's `IF EIBCALEN < 91`. What a program sees past EIBCALEN is the caller's storage after the area + (a local LINK passes its address) or nothing CICS defines -- either way not the program's to read. +- **The harness's model.** A scenario states `"commarea_length": N` (1 to the record's length; never on a scenario + with no COMMAREA): the caller passed the record's first N bytes and no more. Both sides give the program exactly N: + the stub's driver hands the program the area GGCAREA chose -- the N bytes at the end of a page before an + inaccessible one (`commarea.exact`) -- and the Java side states it (`CicsTask` with EIBCALEN N and + `withExactCommarea()`): the det port's DFHCOMMAREA storage holds those N bytes for the task. The COMMAREA a LINKed + program leaves is compared over the fields within the N bytes. +- **Refused, both sides; judged up to it.** A reference past EIBCALEN, read or write, stops the stub at that + statement (the guard page faults: 98, "COMMAREA past EIBCALEN: not modelled") and the det port at the same one + (`DetCics.PastFrom`, "COMMAREA past EIBCALEN (N bytes): not modelled"). As X6, the task is compared up to it and + passes only when both sides refused there (the report's `x6` with `assumes: "X23"`); its end state is not compared. + What z/OS shows there is not claimed. +- **Not covered.** A model port reads its COMMAREA DTO, which holds the whole record: it cannot refuse a reference past + EIBCALEN, so a scenario that makes one fails against a model port (one side refused). Only the level-1 COMMAREA is + exact: a LINK's or an XCTL's COMMAREA keeps the existing models (X10, and the stub's copy of an XCTL's LENGTH bytes), + and a det port LINKing its own DFHCOMMAREA with a LENGTH past N passes what it has (`Cobol.commarea` clamps) where the + stub stops. A guard page needs POSIX (mmap / mprotect): on Windows the stub refuses a stated length. +- **A failing SQL statement's host variables.** The det port binds a statement's input host variables before + DetSql decides whether a planned SQL fault fires; the stub's injected fault reads none. A faulted statement whose + host variables lie past EIBCALEN therefore stops the det port there (X23) and not the stub -- the task fails as + "not the same refusal", never passes. GenApp LGACDB01 (EIBCALEN 90, its INSERT failing) and LGUCDB01 (under 91, its + UPDATE failing) would reach `IF EIBCALEN < 91` that way; on z/OS the statement would read the caller's storage past + the area, so those outcomes stay unreached. +- **Reached.** GenApp's Db2 services: LGACDB01 line 165 ('98', 60 bytes); LGDPDB01 line 143 ('98', 20 bytes) and + WRITE-ERROR-MESSAGE's `EIBCALEN < 91` (40 bytes, the DELETE failing); LGIPDB01's five '98' returns (40 bytes) and + `< 91` (the SELECT failing); LGACDB02 `< 91` (60 bytes, the INSERT failing). Each `< 91` task then LINKs LGSTSQ with + CA-ERROR-MSG and is judged up to X6. Pinned by + `tests/cobol_mainframe/test_equivalence_cics.py` (the guard page, the driver, the length's checks) and + `tests/cobol_mainframe/test_det_translate.py` (the det port's cut storage). + ## Language Environment ### L1. CEEDAYS — MATCHED where documented, REFUSED otherwise diff --git a/gitgalaxy/tools/cobol_to_java/cobol_to_java_transaction_forge.py b/gitgalaxy/tools/cobol_to_java/cobol_to_java_transaction_forge.py index bb67d5351c..222f804b66 100644 --- a/gitgalaxy/tools/cobol_to_java/cobol_to_java_transaction_forge.py +++ b/gitgalaxy/tools/cobol_to_java/cobol_to_java_transaction_forge.py @@ -286,6 +286,7 @@ def commarea_alternative_todos(commarea: dict) -> list[str]: private String abcode = " "; private String termid; // #3989: EIBTRMID (the task's root); null without one private long taskNumber; // #4270: EIBTASKN (the task's root), a stated fact + private boolean exactCommarea; // #4270 (X23): the COMMAREA is EIBCALEN bytes, no more private String exitLabel; // #3989: this level's HANDLE ABEND LABEL private boolean exitActive; private final java.util.ArrayDeque pushedExits = new java.util.ArrayDeque<>(); @@ -634,6 +635,19 @@ def commarea_alternative_todos(commarea: dict) -> list[str]: return this; } + /** #4270 (oracle_assumptions.md X23): the level-1 COMMAREA is exactly EIBCALEN bytes -- a scenario's stated + * `commarea_length`, shorter than the record -- so a reference past EIBCALEN reaches storage the task was never + * given (on z/OS, whatever follows the area). A det port refuses it (DetCics.PastFrom), as the stub does. */ + public CicsTask withExactCommarea() { + this.exactCommarea = true; + return this; + } + + /** Whether this level's COMMAREA is exactly EIBCALEN bytes (X23): level 1 only, when the runner stated it. */ + public boolean exactCommarea() { + return parent == null && exactCommarea; + } + /** EIBTASKN (#4270): the task's number, the same at every LINK / XCTL level. */ public long taskNumber() { return root().taskNumber; diff --git a/gitgalaxy/tools/cobol_to_java/det/cobolrt/Storage.java b/gitgalaxy/tools/cobol_to_java/det/cobolrt/Storage.java index 7cde1fd1a1..62e76248c1 100644 --- a/gitgalaxy/tools/cobol_to_java/det/cobolrt/Storage.java +++ b/gitgalaxy/tools/cobol_to_java/det/cobolrt/Storage.java @@ -4,7 +4,9 @@ /** A COBOL storage area: WORKING-STORAGE, LINKAGE, an FD record or a symbolic map, as bytes. */ public final class Storage { - public final byte[] bytes; + /** Not final for one use (#4270, oracle_assumptions.md X23): a CICS program's DFHCOMMAREA holds, for a task whose + * COMMAREA is shorter than the record (a stated EIBCALEN), only those bytes -- a reference past them fails. */ + public byte[] bytes; /** All zero bytes; the translator's initial image sets every byte. */ public Storage(int size) { diff --git a/gitgalaxy/tools/cobol_to_java/det/program.py b/gitgalaxy/tools/cobol_to_java/det/program.py index a0cd868789..f4a02038c5 100644 --- a/gitgalaxy/tools/cobol_to_java/det/program.py +++ b/gitgalaxy/tools/cobol_to_java/det/program.py @@ -1197,6 +1197,7 @@ def _cics_parts(gen: G.Gen, records: list, roots: dict, proc: S.Procedure, ) dfhca = next((r for r in records if r.section == "LINKAGE" and r.name == "DFHCOMMAREA"), None) ca_in: list[str] = [] + ca_whole: list[str] = [] if dfhca is not None: st = _storage_name(roots[id(dfhca)]) for cls in [x for x in dict.fromkeys([cx.gp.contract, *cx.gp.records.values()]) if x]: @@ -1229,6 +1230,28 @@ def _cics_parts(gen: G.Gen, records: list, roots: dict, proc: S.Procedure, f" caBack = () -> {{ typed.run(); System.arraycopy({st}.bytes, 0, raw, 0, " f"Math.min(raw.length, {st}.bytes.length)); }};", " }"] # fmt: skip + # #4270 (oracle_assumptions.md X23): a level-1 COMMAREA the runner says is exactly EIBCALEN bytes, shorter than + # the record (a scenario's `commarea_length`), is all DFHCOMMAREA holds for the task: a reference past it fails + # (IndexOutOfBounds, refused below as DetCics.PastFrom), as the stub's guard page stops the COBOL side there. + # The record's other bytes come back before the COMMAREA is written to the task's object. + ca_in += [" byte[] caWhole = null;", + f" if (task.exactCommarea() && calen < {st}.bytes.length) {{", + f" caWhole = {st}.bytes;", + f" {st}.bytes = java.util.Arrays.copyOf(caWhole, calen);", + " Runnable typed = caBack;", + " byte[] whole = caWhole;", + " caBack = () -> { caWhole(whole); typed.run(); };", + " }"] # fmt: skip + ca_whole = [ + " /** #4270 (X23): DFHCOMMAREA's whole record again, with what the task left in its EIBCALEN bytes. */", + " private void caWhole(byte[] whole) {", + f" if ({st}.bytes != whole) {{", + f" System.arraycopy({st}.bytes, 0, whole, 0, {st}.bytes.length);", + f" {st}.bytes = whole;", + " }", + " }", + "", + ] store_cases = [f' case "{n}" -> {e};' for n, e in cx.stores.items()] members = [ " private CicsTask task;", @@ -1374,9 +1397,26 @@ def _cics_parts(gen: G.Gen, records: list, roots: dict, proc: S.Procedure, *ca_in, f" Cobol.store({gen.eib('EIBCALEN')}, BigDecimal.valueOf(calen), false, CS);", " try {", - f" {'runAll()' if gen.structured else f'perform(0, {len(proc.paragraphs) - 1})'};", - " } catch (Goback g) {", - " // RETURN / XCTL / an abend ended the program", + *([" try {"] if ca_whole else []), + f" {' ' if ca_whole else ''}{'runAll()' if gen.structured else f'perform(0, {len(proc.paragraphs) - 1})'};", + f" {' ' if ca_whole else ''}}} catch (Goback g) {{", + f" {' ' if ca_whole else ''}// RETURN / XCTL / an abend ended the program", + *( + [ + " }", + " } catch (IndexOutOfBoundsException e) {", + " if (caWhole == null) {", + " throw e;", + " }", + ' throw new DetCics.PastFrom("COMMAREA past EIBCALEN (" + calen + " bytes): not modelled");', + " } finally {", + " if (caWhole != null) {", + " caWhole(caWhole);", + " }", + ] + if ca_whole + else [] + ), " }", " if (!task.ended()) {", " caBack.run();", @@ -1385,7 +1425,7 @@ def _cics_parts(gen: G.Gen, records: list, roots: dict, proc: S.Procedure, " }", "", ] - return members, entry + facades(stub) + return members + ca_whole, entry + facades(stub) def facades(stub: str) -> list[str]: diff --git a/tests/cobol_mainframe/test_det_translate.py b/tests/cobol_mainframe/test_det_translate.py index a754ec6b8a..f00df57318 100644 --- a/tests/cobol_mainframe/test_det_translate.py +++ b/tests/cobol_mainframe/test_det_translate.py @@ -1917,3 +1917,36 @@ def test_a_writeq_length_past_from_is_refused_by_the_det_port_as_by_the_stub_x6( rt = (Path(C.__file__).parent / "cobolrt/cics/DetCics.java").read_text(encoding="utf-8") assert "public static final class PastFrom extends UnsupportedOperationException" in rt assert "public static int within(Field f, int n, String what)" in rt + + +def test_a_commarea_of_a_stated_length_is_all_dfhcommarea_holds(tmp_path): + """#4270 (oracle_assumptions.md X23): a level-1 task whose runner states the COMMAREA is exactly EIBCALEN bytes + (CicsTask.withExactCommarea: a scenario's `commarea_length`, shorter than the record) runs with DFHCOMMAREA's + storage cut to those bytes, so a reference past them fails and is refused as DetCics.PastFrom -- the stub's guard + page stops the COBOL side at the same statement. The record comes back whole before the COMMAREA is written back + to the task's object (caBack), and after the task whatever ended it.""" + pytest.importorskip("tree_sitter_language_pack") + from gitgalaxy.tools.cobol_to_java.det import program as P + + (tmp_path / "T4.cbl").write_text( + " IDENTIFICATION DIVISION.\n PROGRAM-ID. T4.\n DATA DIVISION.\n" + " WORKING-STORAGE SECTION.\n 01 WS-J PIC X(8) VALUE SPACES.\n" + " LINKAGE SECTION.\n 01 DFHCOMMAREA.\n 05 CA-CODE PIC XX.\n" + " 05 CA-REST PIC X(98).\n" + " PROCEDURE DIVISION.\n MAIN-PARA.\n IF EIBCALEN < 100\n" + " MOVE '98' TO CA-CODE\n END-IF\n EXEC CICS RETURN END-EXEC.\n", + encoding="utf-8", + ) + (tmp_path / "proj/src/main/java/com/x").mkdir(parents=True) + stub = "package com.x.service;\nimport com.x.cics.CicsTask;\npublic class T4Service {\n" \ + " public void runTask(CicsTask task) {}\n}\n" # fmt: skip + r = P.translate(tmp_path / "T4.cbl", [], stub, "com.x", {}, tmp_path / "proj") + assert r.stats["holes"] == [] + run = r.java[r.java.index("public void runTask(CicsTask task)") :] + run = " ".join(run[: run.index("\n }\n")].split()) # (the body is indented under the TRUNC wrapper) + assert "if (task.exactCommarea() && calen < " in run and ".bytes = java.util.Arrays.copyOf(caWhole, calen);" in run + assert "caBack = () -> { caWhole(whole); typed.run(); };" in run + assert "} catch (IndexOutOfBoundsException e) { if (caWhole == null) { throw e; }" in run + assert 'throw new DetCics.PastFrom("COMMAREA past EIBCALEN (" + calen + " bytes): not modelled");' in run + assert "} finally { if (caWhole != null) { caWhole(caWhole); } }" in run + assert "private void caWhole(byte[] whole) {" in r.java diff --git a/tests/cobol_mainframe/test_equivalence_cics.py b/tests/cobol_mainframe/test_equivalence_cics.py index bb6d57d3fb..b2c811ad14 100644 --- a/tests/cobol_mainframe/test_equivalence_cics.py +++ b/tests/cobol_mainframe/test_equivalence_cics.py @@ -899,3 +899,124 @@ def test_a_writeq_past_its_from_area_is_judged_up_to_the_refusal_x6(): assert ec.x6_writeq_refusal("WRITEQ TS LENGTH > FROM: not modelled") == "WRITEQ TS LENGTH > FROM: not modelled" assert ec.x6_writeq_refusal("START LENGTH > FROM: not modelled") is None assert ec.x6_writeq_refusal("CEEDAYS picture: not modelled") is None + + +# ---- #4270: a program that takes no COMMAREA; a scenario's stated COMMAREA length (X23) --------------------------- +def test_a_case_says_its_program_takes_no_commarea(tmp_path): + """#4270 (the async credit-card services: an empty LINKAGE SECTION, a det service handleLink()): `"commarea": null` + describes no COMMAREA -- no fields, no DTO, EIBCALEN 0 on both sides. A case that says nothing, or an empty + segment list, is refused by name (it was a KeyError / IndexError), and so is a scenario that gives one anyway.""" + assert ec.commarea_fields(tmp_path, {"commarea": None}) == [] + with pytest.raises(ec.Unsupported, match='"commarea": null for a program that takes none'): + ec.commarea_fields(tmp_path, {}) + with pytest.raises(ec.Unsupported, match="describes nothing"): + ec.commarea_fields(tmp_path, {"commarea": {"segments": []}}) + svc = tmp_path / "GetpolService.java" + svc.write_text("public class GetpolService {\n public void handleLink() {\n }\n}\n", encoding="utf-8") + assert ec.commarea_class({"commarea": None}, tmp_path, svc) is None + import equivalence_java as ej + + (tmp_path / "service").mkdir() + (tmp_path / "service" / f"{ej._service_class('PROG')}.java").write_text("public void handleLink() {}") + case = {**_facade_case(), "clock": "2026/10/01 10:30:15.00", "screens": {}, "commarea": None, "linked": True} + java = ec.cics_equivalence_test(case, tmp_path, []) + assert "Object commarea = null;" in java and "treeToValue" not in java + assert "ScreenModel" not in java # an estate with no BMS map has no screen view models (dto.screen) + assert 'm = method(service, "handleLink", 0);' in java # the facade side enters handleLink() itself + + +def test_a_commarea_in_an_event_of_a_case_with_none_is_refused(tmp_path): + out = tmp_path / "out" + out.mkdir() + (out / "events.txt").write_text("001 RETURN level=1 transid= len=4\n", encoding="ascii") + (out / "001.bin").write_bytes(b"ABCD") + with pytest.raises(ec.Unsupported, match="describes none"): + ec.outputs(out, {"name": "x"}, tmp_path, []) + + +def test_a_scenario_states_a_commarea_shorter_than_its_record(): + """#4270 (X23): `"commarea_length": N` is EIBCALEN -- the caller passed the record's first N bytes and no more + (GenApp's IF EIBCALEN < 91, its '98' returns). 1 to the record's length; never on a scenario with no COMMAREA.""" + fields = [{"name": "CA-A", "offset": 0, "bytes": 2}, {"name": "CA-B", "offset": 2, "bytes": 98}] + assert ec.commarea_length({"name": "s", "commarea": {}}, fields) is None + assert ec.commarea_length({"name": "s", "commarea": {}, "commarea_length": 60}, fields) == 60 + assert ec.commarea_length({"name": "s", "commarea": {}, "commarea_length": 100}, fields) == 100 + for bad in (0, 101, -1, "60", True, 6.0): + with pytest.raises(ec.Unsupported, match="is not a length"): + ec.commarea_length({"name": "s", "commarea": {}, "commarea_length": bad}, fields) + with pytest.raises(ec.Unsupported, match="with no COMMAREA"): + ec.commarea_length({"name": "s", "commarea": None, "commarea_length": 10}, fields) + + +def test_the_driver_runs_the_program_on_the_area_the_stub_chose(): + """#4270 (X23): GGCAREA moves a COMMAREA of a stated length before a guard page; the driver passes whatever area + it chose (LK-CA), and writes back from it.""" + drv = ec.cics_driver("PROG", True) + assert "CALL 'GGCAREA' USING WS-PTR BY VALUE WS-LEN" in drv and "SET ADDRESS OF LK-CA TO WS-PTR" in drv + assert "CALL 'PROG' USING LK-CA" in drv and "CALL 'GGCAOUT' USING LK-CA BY VALUE WS-LEN" in drv + assert "CALL 'PROG'\n" in ec.cics_driver("PROG", False) + assert all(len(ln) <= 72 for ln in drv.splitlines()) + + +def test_a_reference_past_a_stated_eibcalen_is_judged_up_to_it_x22(tmp_path): + """#4270 (X23): the stub's guard-page stop is judged up to, as X6's WRITEQ is; the report says which assumption.""" + said = "COMMAREA past EIBCALEN: not modelled\n" + assert ec.judged_refusal(said) == "COMMAREA past EIBCALEN: not modelled" + assert ec.judged_refusal("WRITEQ TS LENGTH > FROM: not modelled") == "WRITEQ TS LENGTH > FROM: not modelled" + assert ec.judged_refusal("START LENGTH > FROM: not modelled") is None + (tmp_path / "s.x6").write_text("COMMAREA past EIBCALEN (60 bytes): not modelled", encoding="utf-8") + ev = [{"event": "RETURN", "transid": None, "commarea": None}, {"event": "COMMAREA", "commarea": {"A": "1"}}] + cev, jev, x = ec.x6_judged({"name": "s", "prefix_x6": said.strip()}, tmp_path, ev, ev) + assert x == {"cobol": said.strip(), "java": "COMMAREA past EIBCALEN (60 bytes): not modelled", "ok": True, + "assumes": "X23"} # fmt: skip + assert cev == jev == ev[:1] and ec.judged_to(x).startswith("the refused reference past EIBCALEN (X23") + _, _, w = ec.x6_judged({"name": "t", "prefix_x6": "WRITEQ TD LENGTH > FROM: not modelled"}, tmp_path, ev, ev) + assert w["assumes"] == "X6" and not w["ok"] and ec.judged_to(w) == ec.X6_JUDGED + # both sides refused, but not at the same thing (the det port read past EIBCALEN, the stub stopped at the WRITEQ) + _, _, m = ec.x6_judged({"name": "s", "prefix_x6": "WRITEQ TD LENGTH > FROM: not modelled"}, tmp_path, ev, ev) + assert m["assumes"] == "X6" and m["java"].startswith("COMMAREA past EIBCALEN") and not m["ok"] + + +_AREA_MAIN = r""" +#include +#include +#include +int GGCAREA(char **area, int len); +int main(int argc, char **argv) { + char buf[200]; + char *a = buf; + memset(buf, 'x', sizeof buf); + GGCAREA(&a, atoi(argv[1])); + printf("moved=%d first=%c\n", a != buf, a[0]); + fflush(stdout); + a[atoi(argv[2])] = 'y'; + printf("wrote\n"); + return 0; +} +""" + + +@pytest.mark.skipif(__import__("sys").platform == "win32" or not __import__("shutil").which("gcc"), + reason="needs gcc and a POSIX guard page") # fmt: skip +def test_the_stub_stops_a_reference_past_a_stated_commarea_length(tmp_path): + """#4270 (X23): with commarea.exact, GGCAREA copies the COMMAREA to the end of a page before an inaccessible one: + the last byte is the program's, the next stops the run (98, "COMMAREA past EIBCALEN: not modelled"). Without it, + the area is left where the driver has it.""" + import subprocess + + (tmp_path / "main.c").write_text(_AREA_MAIN, encoding="ascii") + exe = tmp_path / "area" + built = subprocess.run(["gcc", "-o", str(exe), str(tmp_path / "main.c"), str(ec.STUB / "ggcics.c")], # noqa: S603,S607 + capture_output=True, text=True, check=False) # fmt: skip + assert built.returncode == 0, built.stderr + d = tmp_path / "d" + d.mkdir() + + def run(*args: str) -> tuple[int, str]: + p = subprocess.run([str(exe), *args], capture_output=True, text=True, env={"GGCICS_DIR": str(d)}, check=False) # noqa: S603 + return p.returncode, p.stdout + + assert run("60", "99") == (0, "moved=0 first=x\nwrote\n") + (d / "commarea.exact").write_text("60\n", encoding="ascii") + assert run("60", "59") == (0, "moved=1 first=x\nwrote\n") + assert run("60", "60") == (98, "moved=1 first=x\nCOMMAREA past EIBCALEN: not modelled\n") diff --git a/tests/equivalence/asynccc-getpol/LICENSE b/tests/equivalence/asynccc-getpol/LICENSE new file mode 100644 index 0000000000..cff851c9c8 --- /dev/null +++ b/tests/equivalence/asynccc-getpol/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 IBM Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tests/equivalence/asynccc-getpol/NOTICE b/tests/equivalence/asynccc-getpol/NOTICE new file mode 100644 index 0000000000..eb2c5aac2c --- /dev/null +++ b/tests/equivalence/asynccc-getpol/NOTICE @@ -0,0 +1,8 @@ +CICS asynchronous API credit-card application example +https://github.com/cicsdev/cics-async-api-credit-card-application-example +Copyright IBM Corp. 2016 + +Licensed under the Apache License, Version 2.0 (see LICENSE, the corpus's own +licence file). The program under test, src/GETPOL.cbl, is the corpus's, used +unmodified from the corpus. case.json (the scenario and its notes) was written +for this equivalence case; it contains no code or data from the corpus. diff --git a/tests/equivalence/asynccc-getpol/case.json b/tests/equivalence/asynccc-getpol/case.json new file mode 100644 index 0000000000..955817551b --- /dev/null +++ b/tests/equivalence/asynccc-getpol/case.json @@ -0,0 +1,21 @@ +{ + "kind": "cics", + "corpus": "cics-async-api-credit-card-application-example", + "program": "GETPOL", + "program_source": "src/GETPOL.cbl", + "copy_dirs": [], + "transid": "GETP", + "clock": "2026/10/01 10:30:15.00", + "datasets": {}, + "commarea": null, + "linked": true, + "screens": {}, + "notes": "IBM's CICS asynchronous API credit-card example: GETPOL (transaction GETP, the customer's policy lookup) is one of the eight services ASYNCPNT starts in parallel (RUN TRANSID) and SEQPNT LINKs to in sequence. It stands in for a slow back end: DELAY FOR SECONDS(2), then RETURN. Its LINKAGE SECTION is empty -- it takes no COMMAREA (\"commarea\": null, #4270: EIBCALEN 0 on both sides; the det service is handleLink()). The RETURN is compared; the DELAY changes nothing a task can see (a task takes no time, oracle_assumptions.md X4) and is not an event on either side. There is no branch. Derived from github.com/cicsdev/cics-async-api-credit-card-application-example (Apache-2.0; Copyright IBM Corp. 2016): LICENSE here is the corpus's own, NOTICE names the source and what was derived.", + "scenarios": [ + { + "name": "delay-then-return", + "commarea": null, + "why": "no COMMAREA (EIBCALEN 0): DELAY FOR SECONDS(2) NORMAL, then RETURN -- the whole program; the RETURN (no TRANSID, no COMMAREA) is its one event" + } + ] +} diff --git a/tests/equivalence/asynccc-getspnd/LICENSE b/tests/equivalence/asynccc-getspnd/LICENSE new file mode 100644 index 0000000000..cff851c9c8 --- /dev/null +++ b/tests/equivalence/asynccc-getspnd/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 IBM Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tests/equivalence/asynccc-getspnd/NOTICE b/tests/equivalence/asynccc-getspnd/NOTICE new file mode 100644 index 0000000000..99920661b0 --- /dev/null +++ b/tests/equivalence/asynccc-getspnd/NOTICE @@ -0,0 +1,8 @@ +CICS asynchronous API credit-card application example +https://github.com/cicsdev/cics-async-api-credit-card-application-example +Copyright IBM Corp. 2016 + +Licensed under the Apache License, Version 2.0 (see LICENSE, the corpus's own +licence file). The program under test, src/GETSPND.cbl, is the corpus's, used +unmodified from the corpus. case.json (the scenario and its notes) was written +for this equivalence case; it contains no code or data from the corpus. diff --git a/tests/equivalence/asynccc-getspnd/case.json b/tests/equivalence/asynccc-getspnd/case.json new file mode 100644 index 0000000000..8a06b07f2b --- /dev/null +++ b/tests/equivalence/asynccc-getspnd/case.json @@ -0,0 +1,21 @@ +{ + "kind": "cics", + "corpus": "cics-async-api-credit-card-application-example", + "program": "GETSPND", + "program_source": "src/GETSPND.cbl", + "copy_dirs": [], + "transid": "SPND", + "clock": "2026/10/01 10:30:15.00", + "datasets": {}, + "commarea": null, + "linked": true, + "screens": {}, + "notes": "IBM's CICS asynchronous API credit-card example: GETSPND (transaction SPND, the customer's spending lookup) is one of the eight services ASYNCPNT starts in parallel (RUN TRANSID) and SEQPNT LINKs to in sequence. It stands in for a slow back end: DELAY FOR SECONDS(2), then RETURN. Its LINKAGE SECTION is empty -- it takes no COMMAREA (\"commarea\": null, #4270: EIBCALEN 0 on both sides; the det service is handleLink()). The RETURN is compared; the DELAY changes nothing a task can see (a task takes no time, oracle_assumptions.md X4) and is not an event on either side. There is no branch. Derived from github.com/cicsdev/cics-async-api-credit-card-application-example (Apache-2.0; Copyright IBM Corp. 2016): LICENSE here is the corpus's own, NOTICE names the source and what was derived.", + "scenarios": [ + { + "name": "delay-then-return", + "commarea": null, + "why": "no COMMAREA (EIBCALEN 0): DELAY FOR SECONDS(2) NORMAL, then RETURN -- the whole program; the RETURN (no TRANSID, no COMMAREA) is its one event" + } + ] +} diff --git a/tests/equivalence/asynccc-updcsdb/LICENSE b/tests/equivalence/asynccc-updcsdb/LICENSE new file mode 100644 index 0000000000..cff851c9c8 --- /dev/null +++ b/tests/equivalence/asynccc-updcsdb/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 IBM Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tests/equivalence/asynccc-updcsdb/NOTICE b/tests/equivalence/asynccc-updcsdb/NOTICE new file mode 100644 index 0000000000..c7dd802254 --- /dev/null +++ b/tests/equivalence/asynccc-updcsdb/NOTICE @@ -0,0 +1,8 @@ +CICS asynchronous API credit-card application example +https://github.com/cicsdev/cics-async-api-credit-card-application-example +Copyright IBM Corp. 2016 + +Licensed under the Apache License, Version 2.0 (see LICENSE, the corpus's own +licence file). The program under test, src/UPDCSDB.cbl, is the corpus's, used +unmodified from the corpus. case.json (the scenario and its notes) was written +for this equivalence case; it contains no code or data from the corpus. diff --git a/tests/equivalence/asynccc-updcsdb/case.json b/tests/equivalence/asynccc-updcsdb/case.json new file mode 100644 index 0000000000..f452de1bc5 --- /dev/null +++ b/tests/equivalence/asynccc-updcsdb/case.json @@ -0,0 +1,21 @@ +{ + "kind": "cics", + "corpus": "cics-async-api-credit-card-application-example", + "program": "UPDCSDB", + "program_source": "src/UPDCSDB.cbl", + "copy_dirs": [], + "transid": "UPDB", + "clock": "2026/10/01 10:30:15.00", + "datasets": {}, + "commarea": null, + "linked": true, + "screens": {}, + "notes": "IBM's CICS asynchronous API credit-card example: UPDCSDB (transaction UPDB, the customer-database cache update) is one of the eight services ASYNCPNT starts in parallel (RUN TRANSID) and SEQPNT LINKs to in sequence. It stands in for a slow back end: DELAY FOR SECONDS(3), then RETURN. Its LINKAGE SECTION is empty -- it takes no COMMAREA (\"commarea\": null, #4270: EIBCALEN 0 on both sides; the det service is handleLink()). The RETURN is compared; the DELAY changes nothing a task can see (a task takes no time, oracle_assumptions.md X4) and is not an event on either side. There is no branch. Derived from github.com/cicsdev/cics-async-api-credit-card-application-example (Apache-2.0; Copyright IBM Corp. 2016): LICENSE here is the corpus's own, NOTICE names the source and what was derived.", + "scenarios": [ + { + "name": "delay-then-return", + "commarea": null, + "why": "no COMMAREA (EIBCALEN 0): DELAY FOR SECONDS(3) NORMAL, then RETURN -- the whole program; the RETURN (no TRANSID, no COMMAREA) is its one event" + } + ] +} diff --git a/tests/equivalence/cics/ggcics.c b/tests/equivalence/cics/ggcics.c index 3fc4f5a549..288c7e21eb 100644 --- a/tests/equivalence/cics/ggcics.c +++ b/tests/equivalence/cics/ggcics.c @@ -5,6 +5,8 @@ * * Inputs come from $GGCICS_DIR: * commarea.in the COMMAREA the task starts with (its length is EIBCALEN) + * commarea.exact #4270 (X23): the COMMAREA is exactly that long (a scenario's stated + * length, shorter than the record): GGCAREA puts it before a guard page * receive_.bin what RECEIVE MAP() returns; absent means MAPFAIL * terminal.in what an unformatted terminal RECEIVE returns (#4005: the * step's text, typed on a cleared screen); absent means the @@ -67,6 +69,9 @@ static struct tm *gg_gmtime_r(const time_t *when, struct tm *out) { #define gmtime_r gg_gmtime_r #else #define MKDIR(p) mkdir((p), 0777) +#include /* #4270 (X23): GGCAREA's guard page */ +#include +#include #endif typedef struct { @@ -133,6 +138,50 @@ int GGCLOAD(char *area, int maxlen) { return n; } +/* #4270 (oracle_assumptions.md X23): a COMMAREA of a stated length (commarea.exact) is exactly that many bytes + * -- the caller passed no more. GGCAREA copies it to the end of a page followed by an inaccessible one, so a + * reference past EIBCALEN, read or write, faults at that statement: the run stops there as not modelled (on z/OS + * the program sees whatever storage follows the area) and the task is judged up to it, as the det port refuses + * the same reference (DetCics.PastFrom). Any other COMMAREA stays in the driver's own area (*area unchanged). */ +static void refuse(const char *what); +#ifndef _WIN32 +static char *guard_page; +static size_t guard_size; +static void past_eibcalen(int sig, siginfo_t *info, void *ctx) { + char *at = (char *)info->si_addr; + (void)ctx; + if (at >= guard_page && at < guard_page + guard_size) refuse("COMMAREA past EIBCALEN"); + signal(sig, SIG_DFL); /* any other fault is the stub's or the program's own: as it was */ + raise(sig); +} +#endif + +int GGCAREA(char **area, int len) { + char path[4096]; + struct stat st; + snprintf(path, sizeof path, "%s/commarea.exact", dir_in()); + if (len <= 0 || stat(path, &st) != 0) return 0; +#ifdef _WIN32 + refuse("a stated COMMAREA length on this platform"); +#else + size_t page = (size_t)sysconf(_SC_PAGESIZE); + size_t body = ((size_t)len + page - 1) / page * page; + char *base = mmap(NULL, body + page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED || mprotect(base + body, page, PROT_NONE) != 0) refuse("a guarded COMMAREA"); + guard_page = base + body; + guard_size = page; + memcpy(guard_page - len, *area, (size_t)len); + *area = guard_page - len; + struct sigaction sa; + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = past_eibcalen; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); +#endif + return 0; +} + /* #4023 follow-up: the planned condition of this command on this file, if faults.cfg plans one. Every * command counts (per task: a task is one process), whether or not it faults. */ static int fault_counts[32]; diff --git a/tests/equivalence/det_sweep_coverage.json b/tests/equivalence/det_sweep_coverage.json index f4cd622973..41177ffebd 100644 --- a/tests/equivalence/det_sweep_coverage.json +++ b/tests/equivalence/det_sweep_coverage.json @@ -2,6 +2,57 @@ "format": "det-sweep-coverage/1", "about": "#4270: the paragraph / branch coverage of each det-proven case, as `proof_sweep.py --det-only` reported it; written only by `tests/tools/det_coverage_ledger.py update SWEEP_DIR...`, checked by CI's det-sweep. An entry is trusted only while its input fingerprints match the tree (proof_blockers.py); stale or missing stays unknown.", "cases": { + "asynccc-getpol": { + "scenarios": 1, + "paragraphs": { + "covered": 1, + "live": 1 + }, + "branches": { + "covered": 0, + "total": 0 + }, + "inputs": { + "case": "8715eb31cb1fe3d43aec7c2ce9de9d3b6e643b65113edc51fb979c8d39d43e21", + "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "asynccc-getspnd": { + "scenarios": 1, + "paragraphs": { + "covered": 1, + "live": 1 + }, + "branches": { + "covered": 0, + "total": 0 + }, + "inputs": { + "case": "3f15c0e7364770e323f6e9c65487c146dbfba37ff81f38d42b9aebc930125c49", + "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "asynccc-updcsdb": { + "scenarios": 1, + "paragraphs": { + "covered": 1, + "live": 1 + }, + "branches": { + "covered": 0, + "total": 0 + }, + "inputs": { + "case": "6f6238b16a7d8e202086190913f64017584104833d6a343623c615faa618d8ac", + "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "carddemo-acctupdate": { "scenarios": 137, "paragraphs": { @@ -326,37 +377,37 @@ } }, "genapp-lgacdb01": { - "scenarios": 8, + "scenarios": 9, "paragraphs": { "covered": 4, "live": 4 }, "branches": { - "covered": 12, + "covered": 13, "total": 14 }, "inputs": { - "case": "a320c76c6affa7cc648bbd96dbe5caf6d9bcc9034c77001bec9ae94fd7b7b0a7", + "case": "253e59e79e236523d89d2d0ab74c4347955c262c85ae2e7f658230152c319c13", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "e71654ea54ac3af1006c036a906721d46a3a57d9f8b5c82bb187fde858e7eade", - "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, "genapp-lgacdb02": { - "scenarios": 10, + "scenarios": 11, "paragraphs": { "covered": 3, "live": 3 }, "branches": { - "covered": 9, + "covered": 10, "total": 10 }, "inputs": { - "case": "392d194fd6e5ad0a29843ad09f787798224870ec09c16f60282b173620fd141a", + "case": "a3ae0bfb1c47fd295bc6814406a2d998597903c43943999916a5aefc93eebb76", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "e71654ea54ac3af1006c036a906721d46a3a57d9f8b5c82bb187fde858e7eade", - "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, "genapp-lgacvs01": { @@ -394,20 +445,20 @@ } }, "genapp-lgdpdb01": { - "scenarios": 10, + "scenarios": 12, "paragraphs": { "covered": 3, "live": 3 }, "branches": { - "covered": 10, + "covered": 12, "total": 12 }, "inputs": { - "case": "ab67602423131993fbad35735341b48e6d5a13b1e750281c7e14f44df804d2f1", + "case": "551cd3e514845511da462dd0e64d6542d64ae2b98d2756cd8d31e2f4c9f0c373", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "e71654ea54ac3af1006c036a906721d46a3a57d9f8b5c82bb187fde858e7eade", - "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, "genapp-lgdpvs01": { @@ -428,20 +479,20 @@ } }, "genapp-lgipdb01": { - "scenarios": 49, + "scenarios": 55, "paragraphs": { "covered": 12, "live": 12 }, "branches": { - "covered": 67, + "covered": 73, "total": 74 }, "inputs": { - "case": "dff142773f622e5b14409734355218ce133190d30fec3a58741b3774bac15963", + "case": "ed0171d8c5324bfa7a5abd7a11613602d4192660eb6cb0045e52558a87068431", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "e71654ea54ac3af1006c036a906721d46a3a57d9f8b5c82bb187fde858e7eade", - "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, "genapp-lgucdb01": { @@ -455,10 +506,10 @@ "total": 10 }, "inputs": { - "case": "d69fa969a4d19c768ebb623dc5e322c0e0728ae96fad8764e982e5391cd29563", + "case": "d690be3b361358cb5c8ce9e490a5281f4b2aa164b023d5c18c09fec94fc4a651", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "e71654ea54ac3af1006c036a906721d46a3a57d9f8b5c82bb187fde858e7eade", - "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" + "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, "genapp-lgucvs01": { diff --git a/tests/equivalence/genapp-lgacdb01/case.json b/tests/equivalence/genapp-lgacdb01/case.json index 56249ef977..e89dd32c65 100644 --- a/tests/equivalence/genapp-lgacdb01/case.json +++ b/tests/equivalence/genapp-lgacdb01/case.json @@ -115,7 +115,20 @@ "name": "no-commarea", "commarea": null, "why": "EIBCALEN 0: IF EIBCALEN IS EQUAL TO ZERO -- EM-VARIABLE ' NO COMMAREA RECEIVED', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG (it writes the message to TD CSMT and TS GENAERRS: #4607), IF EIBCALEN > 0 is false, then ABEND LGCA" + }, + { + "name": "short-commarea", + "counters": { + "GENA/GENACUSTNUM": 1000011 + }, + "commarea": { + "CA-REQUEST-ID": "01ACUS", + "CA-CUSTOMER-NUM": "0", + "CA-REQUEST-SPECIFIC": "Ada Lovelace 1815-12-10Ockham Park 12 KT246HP 00007700900123 01483222111 ada@analytical.example " + }, + "commarea_length": 60, + "why": "#4270 (X23): the caller passed 60 bytes, fewer than the 90 (WS-CA-HEADER-LEN 18 + WS-CUSTOMER-LEN 72) the program requires: IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 165) is true -- CA-RETURN-CODE '98', RETURN; no GET COUNTER, no INSERT. The COMMAREA it leaves is compared over its 60 bytes" } ], - "notes": "GenApp's add-customer service (LGACDB01, LINKed with the LGCMAREA COMMAREA, described by its caller LGACUS01's record): the customer number is the named counter GENACUSTNUM in pool GENA (GET COUNTER), else Db2's identity (INSERT ... DEFAULT, then SET :H = IDENTITY_VAL_LOCAL()); the CUSTOMER row is INSERTed, LGACVS01 is LINKed to write the VSAM copy and LGACDB02 to add the CUSTOMER_SECURE row (default password). The case runs LGACVS01 and LGACDB02 in the same task; each scenario says which named counters the region has. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA)." + "notes": "GenApp's add-customer service (LGACDB01, LINKed with the LGCMAREA COMMAREA, described by its caller LGACUS01's record): the customer number is the named counter GENACUSTNUM in pool GENA (GET COUNTER), else Db2's identity (INSERT ... DEFAULT, then SET :H = IDENTITY_VAL_LOCAL()); the CUSTOMER row is INSERTed, LGACVS01 is LINKed to write the VSAM copy and LGACDB02 to add the CUSTOMER_SECURE row (default password). The case runs LGACVS01 and LGACDB02 in the same task; each scenario says which named counters the region has. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA). #4270 (X23): short-commarea gives the program 60 of the 90 bytes it requires (line 165 true: '98'). WRITE-ERROR-MESSAGE's IF EIBCALEN < 91 true needs EIBCALEN 90 exactly and a failing INSERT; the INSERT's host variables (CA-EMAIL-ADDRESS ...) lie past byte 90, so the task references past EIBCALEN -- refused by X23 (the det port binds them before the statement runs; the stub's injected SQL fault reads none). Not reached." } diff --git a/tests/equivalence/genapp-lgacdb02/case.json b/tests/equivalence/genapp-lgacdb02/case.json index 9a3d83c029..9ac772c249 100644 --- a/tests/equivalence/genapp-lgacdb02/case.json +++ b/tests/equivalence/genapp-lgacdb02/case.json @@ -161,7 +161,27 @@ "name": "no-commarea", "commarea": null, "why": "EIBCALEN 0: IF EIBCALEN IS EQUAL TO ZERO -- EM-VARIABLE ' NO COMMAREA RECEIVED', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG (it writes the message to TD CSMT and TS GENAERRS: #4607), IF EIBCALEN > 0 is false, then ABEND LGCA" + }, + { + "name": "short-commarea-insert-fails", + "commarea": { + "D2-REQUEST-ID": "02ACUS", + "D2-RETURN-CODE": "00", + "D2-CUSTOMER-NUM": 11, + "D2-CUSTSECR-PASS": "5732fec825535eeafb8fac50fee3a8aa", + "D2-CUSTSECR-COUNT": "0000", + "D2-CUSTSECR-STATE": "N", + "D2-CUSTSECR-DATA": "" + }, + "commarea_length": 60, + "sql_faults": [ + { + "line": 166, + "sqlcode": -803 + } + ], + "why": "#4270 (X23): the caller passed 60 bytes (EIBCALEN 60; every field the INSERT reads lies in them) and INSERT-CUSTOMER-PASSWORD's INSERT (line 166) fails -803: '98', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG, then IF EIBCALEN < 91 is true -- DFHCOMMAREA(1:60) into CA-DATA and the second LINK, whose 104-byte WRITEQ from LGSTSQ's 95-byte WRITE-MSG is refused (X6): judged up to it" } ], - "notes": "GenApp's Db2 'add customer password' (LGACDB02, LINKed by LGACDB01 after its INSERT INTO CUSTOMER, with D2-CUSTOMER-NUM the new customer): '02ACUS' INSERTs one CUSTOMER_SECURE row from D2-CUSTOMER-NUM (9(10) -> S9(9) COMP), D2-CUSTSECR-PASS, D2-CUSTSECR-STATE and D2-CUSTSECR-COUNT (X(4) -> S9(9) COMP); any other request id sets D2-RETURN-CODE '99' and RETURNs. The COMMAREA (32500 bytes, LGACDB02's own DFHCOMMAREA) and the CUSTOMER / CUSTOMER_SECURE tables are compared after each task. Seed: every INSERT of GenApp's base/cntl/db2cre.jcl, plus one CUSTOMER row (11) this case adds -- GenApp's ten customers each already have a CUSTOMER_SECURE row, so with GenApp's rows alone the INSERT could only fail; customer 11 stands for the row LGACDB01 has just inserted. A failing INSERT (the auto SQL fault, -803) and a zero-length COMMAREA go to WRITE-ERROR-MESSAGE, which LINKs LGSTSQ (below). Derived from the CICS General Insurance Application (GenApp, github.com/cicsdev/cics-genapp; (C) Copyright IBM Corp. 2011, 2020), licensed under the Eclipse Public License 2.0: LICENSE here is the corpus's own, NOTICE names the source and what was derived. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA)." + "notes": "GenApp's Db2 'add customer password' (LGACDB02, LINKed by LGACDB01 after its INSERT INTO CUSTOMER, with D2-CUSTOMER-NUM the new customer): '02ACUS' INSERTs one CUSTOMER_SECURE row from D2-CUSTOMER-NUM (9(10) -> S9(9) COMP), D2-CUSTSECR-PASS, D2-CUSTSECR-STATE and D2-CUSTSECR-COUNT (X(4) -> S9(9) COMP); any other request id sets D2-RETURN-CODE '99' and RETURNs. The COMMAREA (32500 bytes, LGACDB02's own DFHCOMMAREA) and the CUSTOMER / CUSTOMER_SECURE tables are compared after each task. Seed: every INSERT of GenApp's base/cntl/db2cre.jcl, plus one CUSTOMER row (11) this case adds -- GenApp's ten customers each already have a CUSTOMER_SECURE row, so with GenApp's rows alone the INSERT could only fail; customer 11 stands for the row LGACDB01 has just inserted. A failing INSERT (the auto SQL fault, -803) and a zero-length COMMAREA go to WRITE-ERROR-MESSAGE, which LINKs LGSTSQ (below). Derived from the CICS General Insurance Application (GenApp, github.com/cicsdev/cics-genapp; (C) Copyright IBM Corp. 2011, 2020), licensed under the Eclipse Public License 2.0: LICENSE here is the corpus's own, NOTICE names the source and what was derived. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). #4270 (X23): a scenario's commarea_length gives the program a COMMAREA shorter than its record -- exactly that many bytes on both sides (a reference past them is refused and the task judged up to it): short-commarea-insert-fails reaches IF EIBCALEN < 91 true." } diff --git a/tests/equivalence/genapp-lgdpdb01/case.json b/tests/equivalence/genapp-lgdpdb01/case.json index 4f8447949b..fe73f570bc 100644 --- a/tests/equivalence/genapp-lgdpdb01/case.json +++ b/tests/equivalence/genapp-lgdpdb01/case.json @@ -127,6 +127,34 @@ "name": "no-commarea", "commarea": null, "why": "EIBCALEN 0: IF EIBCALEN IS EQUAL TO ZERO -- EM-VARIABLE ' NO COMMAREA RECEIVED', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG (it writes the message to TD CSMT and TS GENAERRS: #4607), IF EIBCALEN > 0 is false, then ABEND LGCA" + }, + { + "name": "short-commarea", + "commarea": { + "CA-REQUEST-ID": "01DEND", + "CA-RETURN-CODE": "00", + "CA-CUSTOMER-NUM": 3, + "CA-REQUEST-SPECIFIC": "0000000005 " + }, + "commarea_length": 20, + "why": "#4270 (X23): the caller passed 20 bytes, fewer than WS-CA-HEADER-LEN (28): IF EIBCALEN IS LESS THAN WS-CA-HEADER-LEN (line 143) is true -- '98', RETURN; no DELETE, no LINK. The COMMAREA it leaves is compared over its 20 bytes" + }, + { + "name": "short-commarea-delete-fails", + "commarea": { + "CA-REQUEST-ID": "01DEND", + "CA-RETURN-CODE": "00", + "CA-CUSTOMER-NUM": 3, + "CA-REQUEST-SPECIFIC": "0000000005 " + }, + "commarea_length": 40, + "sql_faults": [ + { + "line": 189, + "sqlcode": -904 + } + ], + "why": "#4270 (X23): 40 bytes (the 28-byte header passed): DELETE-POLICY-DB2-INFO's DELETE (line 189) fails -904: '90', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG, then IF EIBCALEN < 91 is true -- DFHCOMMAREA(1:40) into CA-DATA and the second LINK, whose 104-byte WRITEQ is refused (X6): judged up to it" } ], "notes": "GenApp's Db2 'delete policy' (LGDPDB01): for 01DEND / 01DHOU / 01DCOM / 01DMOT it DELETEs the POLICY row (CUSTOMERNUMBER, POLICYNUMBER), which cascades to ENDOWMENT / HOUSE / MOTOR / COMMERCIAL / CLAIM, then EXEC CICS LINK PROGRAM(LGDPVS01) to delete the VSAM copy; any other request id sets CA-RETURN-CODE '99'. The COMMAREA (LGCMAREA, 32500 bytes; its layout read from the caller LGDPOL01's DFHCOMMAREA, COPY LGCMAREA, as LGDPDB01's own brings it in by EXEC SQL INCLUDE) and every table the DELETE's cascade reaches are compared after each task; seed: GenApp's own INSERTs (base/cntl/db2cre.jcl). NOT exercised -- refused by the harness, which runs one program: every accepted request id ends in LINK PROGRAM(LGDPVS01) (whether the DELETE deleted a row or not), and a failing DELETE (+100 for a policy not on file, which LGDPDB01 treats as an error: '90') and a zero-length COMMAREA go first to LINK PROGRAM('LGSTSQ'). So the DELETE itself, its cascade and '90' need a model of the LINKed programs. The EIBCALEN < 28 check ('98') is unreachable with the 32500-byte COMMAREA the case passes. Derived from the CICS General Insurance Application (GenApp, github.com/cicsdev/cics-genapp; (C) Copyright IBM Corp. 2011, 2020), licensed under the Eclipse Public License 2.0: LICENSE here is the corpus's own, NOTICE names the source and what was derived. With LGDPVS01 run in the same task (\"programs\"), the delete path is proven too: DELETE FROM POLICY, its ON DELETE CASCADE into the type tables, and the VSAM delete. A delete that finds no row (+100 -> '90') LINKs LGSTSQ (below). #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA).", diff --git a/tests/equivalence/genapp-lgipdb01/case.json b/tests/equivalence/genapp-lgipdb01/case.json index 9a79462c57..31ea34c681 100644 --- a/tests/equivalence/genapp-lgipdb01/case.json +++ b/tests/equivalence/genapp-lgipdb01/case.json @@ -453,6 +453,72 @@ } ], "why": "zip-cursor-so212jn, with CLOSE Zip_Cursor (line 935) failing SQLCODE -904 (resource unavailable): IF SQLCODE NOT EQUAL 0 -- '88', WRITE-ERROR-MESSAGE LINKs LGSTSQ twice; the second (CA-ERROR-MSG, 99 bytes) has LGSTSQ write 104 bytes from its 95-byte WRITE-MSG, so the task is judged up to that WRITEQ (X6)" + }, + { + "name": "endowment-short-commarea", + "commarea": { + "CA-REQUEST-ID": "01IEND", + "CA-CUSTOMER-NUM": 3, + "CA-REQUEST-SPECIFIC": "0000000005 " + }, + "commarea_length": 40, + "why": "#4270 (X23): endowment-3-5 with a 40-byte COMMAREA (the header and policy number passed): the SELECT is +0, then IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 390) is true -- '98', RETURN, nothing moved into the COMMAREA. The COMMAREA it leaves is compared over its 40 bytes" + }, + { + "name": "house-short-commarea", + "commarea": { + "CA-REQUEST-ID": "01IHOU", + "CA-CUSTOMER-NUM": 4, + "CA-REQUEST-SPECIFIC": "0000000006 " + }, + "commarea_length": 40, + "why": "#4270 (X23): house-4-6 with a 40-byte COMMAREA (the header and policy number passed): the SELECT is +0, then IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 487) is true -- '98', RETURN, nothing moved into the COMMAREA. The COMMAREA it leaves is compared over its 40 bytes" + }, + { + "name": "motor-short-commarea", + "commarea": { + "CA-REQUEST-ID": "01IMOT", + "CA-CUSTOMER-NUM": 2, + "CA-REQUEST-SPECIFIC": "0000000001 " + }, + "commarea_length": 40, + "why": "#4270 (X23): motor-2-1 with a 40-byte COMMAREA (the header and policy number passed): the SELECT is +0, then IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 581) is true -- '98', RETURN, nothing moved into the COMMAREA. The COMMAREA it leaves is compared over its 40 bytes" + }, + { + "name": "commercial-short-commarea", + "commarea": { + "CA-REQUEST-ID": "01ICOM", + "CA-CUSTOMER-NUM": 1, + "CA-REQUEST-SPECIFIC": "0000000010 " + }, + "commarea_length": 40, + "why": "#4270 (X23): commercial-1-10 with a 40-byte COMMAREA (the header and policy number passed): the SELECT is +0, then IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 689) is true -- '98', RETURN, nothing moved into the COMMAREA. The COMMAREA it leaves is compared over its 40 bytes" + }, + { + "name": "commercial-by-policy-short-commarea", + "commarea": { + "CA-REQUEST-ID": "02ICOM", + "CA-CUSTOMER-NUM": 7, + "CA-REQUEST-SPECIFIC": "0000000010 " + }, + "commarea_length": 40, + "why": "#4270 (X23): commercial-by-policy-10 with a 40-byte COMMAREA (the header and policy number passed): the SELECT is +0, then IF EIBCALEN IS LESS THAN WS-REQUIRED-CA-LEN (line 793) is true -- '98', RETURN, nothing moved into the COMMAREA. The COMMAREA it leaves is compared over its 40 bytes" + }, + { + "name": "endowment-short-commarea-select-fails", + "commarea": { + "CA-REQUEST-ID": "01IEND", + "CA-CUSTOMER-NUM": 3, + "CA-REQUEST-SPECIFIC": "0000000005 " + }, + "sql_faults": [ + { + "line": 330, + "sqlcode": -904 + } + ], + "commarea_length": 40, + "why": "#4270 (X23): endowment-select-fails with a 40-byte COMMAREA: the SELECT (line 330) fails -904: '90', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG, then IF EIBCALEN < 91 is true -- DFHCOMMAREA(1:40) into CA-DATA and the second LINK, whose 104-byte WRITEQ is refused (X6): judged up to it" } ], "notes": "GenApp's Db2 'inquire policy' (LGIPDB01, LINKed by LGIPOL01 with the 32500-byte LGCMAREA COMMAREA; the COMMAREA is described by the record the caller passes, LGIPOL01's DFHCOMMAREA (COPY LGCMAREA), since LGIPDB01 includes the same copybook with EXEC SQL INCLUDE: the generated DTO is the caller's): the request id, upper-cased, picks the inquiry -- 01IEND / 01IHOU / 01IMOT / 01ICOM a SELECT INTO on POLICY joined with ENDOWMENT / HOUSE / MOTOR / COMMERCIAL by customer and policy number; 02ICOM a commercial SELECT by policy number alone (returning the customer number); 03ICOM the scroll cursor Cust_Cursor over a customer's commercial policies; 05ICOM the cursor Zip_Cursor over COMMERCIAL rows with CA-B-POSTCODE; anything else '99'. A row found is moved into CA-POLICY-COMMON and the type's area, marked 'FINAL'; +100 gives '01'. Tables (unqualified: the bind's QUALIFIER, GENASA1) are GenApp's own (base/cntl/db2cre.jcl: DDL and INSERTs, = GENASA1) and compared after every scenario. Seed: @case/seed.sql, every INSERT of db2cre.jcl plus customer 11 (as genapp-lgacdb02's) and three policies of customer 11 with brokerId and payment NULL, the endowment one with PADDINGDATA set (GenApp's own INSERTs never leave them NULL or set it). Not reachable here: a COMMAREA too short for the policy ('98', l.390-392, 487-489, 581-583, 689-691, 793-795) -- the harness passes the whole 32500-byte COMMAREA; OPEN failing ('89' l.843-847, 926-930: no declared fault yet); more than one row from either cursor and the 20-row cap (l.907-908) -- each seeded customer and zipcode has at most one commercial policy. Derived from the CICS General Insurance Application (GenApp, github.com/cicsdev/cics-genapp; (C) Copyright IBM Corp. 2011, 2021), licensed under the Eclipse Public License 2.0: LICENSE here is the corpus's own, NOTICE names the source and what was derived. A negative SQLCODE on each SELECT INTO ('90', l.426-428 and its copies) and on each CLOSE ('88', l.855-859, 938-942) is a declared SQL fault (-904, M2): the auto fault of a SELECT INTO is +100 and a CLOSE gets none. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA)." diff --git a/tests/equivalence/genapp-lgucdb01/case.json b/tests/equivalence/genapp-lgucdb01/case.json index 290feb50dc..6be08ec563 100644 --- a/tests/equivalence/genapp-lgucdb01/case.json +++ b/tests/equivalence/genapp-lgucdb01/case.json @@ -112,5 +112,5 @@ "why": "EIBCALEN 0: IF EIBCALEN IS EQUAL TO ZERO -- EM-VARIABLE ' NO COMMAREA RECEIVED', WRITE-ERROR-MESSAGE LINKs LGSTSQ with ERROR-MSG (it writes the message to TD CSMT and TS GENAERRS: #4607), IF EIBCALEN > 0 is false, then ABEND LGCA" } ], - "notes": "GenApp's update-customer service (LGUCDB01, LINKed with the LGCMAREA COMMAREA, described by its caller LGUCUS01's record): UPDATE CUSTOMER sets every column from the COMMAREA by customer number, then LGUCVS01 (run in the same task) rewrites the VSAM copy. A customer that does not exist (+100, '01') leads LGUCVS01 to LINK LGSTSQ (below). A customer on KSDSCUST but not in Db2 (customer 11, ksdscust-vsam-only.txt) is the one +100 that does not reach LGSTSQ: LGUCVS01 finds the record. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA)." + "notes": "GenApp's update-customer service (LGUCDB01, LINKed with the LGCMAREA COMMAREA, described by its caller LGUCUS01's record): UPDATE CUSTOMER sets every column from the COMMAREA by customer number, then LGUCVS01 (run in the same task) rewrites the VSAM copy. A customer that does not exist (+100, '01') leads LGUCVS01 to LINK LGSTSQ (below). A customer on KSDSCUST but not in Db2 (customer 11, ksdscust-vsam-only.txt) is the one +100 that does not reach LGSTSQ: LGUCVS01 finds the record. #4607 / X6 (owner decision): LGSTSQ runs in the same task (\"programs\"; \"region\" for its ASSIGN SYSID). An EIBCALEN-0 task (no-commarea) LINKs it with the short ERROR-MSG and ABENDs LGCA, compared whole (the WRITEQ TD and WRITEQ TS it writes too). An error path with the 32500-byte COMMAREA LINKs it a second time with the 99-byte CA-ERROR-MSG; LGSTSQ then writes 104 bytes from its 95-byte WRITE-MSG (X6), so that task is judged up to the refused WRITEQ: what it did before is compared, its end state is not. Not settled on z/OS (#4050). Still not reached: IF EIBCALEN < 91 true (a COMMAREA shorter than 91 bytes; the harness passes the case's whole COMMAREA). #4270 (X23): IF EIBCALEN < 91 true needs a failing UPDATE with EIBCALEN under 91, but the UPDATE's host variables (CA-FIRST-NAME ... CA-EMAIL-ADDRESS) lie past byte 90, so the task references past EIBCALEN -- refused by X23 (the det port binds them before the statement runs; the stub's injected SQL fault reads none). Not reached." } diff --git a/tests/tools/equivalence_cics.py b/tests/tools/equivalence_cics.py index 21fa2678cd..0e3fa0d24d 100644 --- a/tests/tools/equivalence_cics.py +++ b/tests/tools/equivalence_cics.py @@ -917,6 +917,7 @@ def cics_driver(program: str, has_commarea: bool) -> str: " 05 IN-TIME PIC 9(7).", "WORKING-STORAGE SECTION.", "COPY DFHEIBLK.", "COPY DFHAID.", "01 WS-CA PIC X(32767).", "01 WS-LEN PIC S9(9) COMP-5.", + "01 WS-PTR USAGE POINTER.", "LINKAGE SECTION.", "01 LK-CA PIC X(32767).", "PROCEDURE DIVISION.", " OPEN INPUT EIB-IN", " READ EIB-IN", " CLOSE EIB-IN", " INITIALIZE DFHEIBLK GG-CICS", @@ -925,11 +926,16 @@ def cics_driver(program: str, has_commarea: bool) -> str: " MOVE 0 TO GG-NUM", " EVALUATE IN-AID"] # fmt: skip lines += [f" WHEN '{n}' MOVE {n} TO EIBAID" for n in aid_names] + # #4270 (X23): GGCAREA moves a COMMAREA of a stated length (commarea.exact) to exactly that many bytes before a + # guard page, and leaves any other where it is (WS-CA) lines += [" END-EVALUATE", " MOVE LOW-VALUES TO WS-CA", " CALL 'GGCLOAD' USING WS-CA BY VALUE LENGTH OF WS-CA", " RETURNING WS-LEN", " MOVE WS-LEN TO EIBCALEN", - f" CALL '{program}'" + (" USING WS-CA" if has_commarea else ""), - " CALL 'GGCAOUT' USING WS-CA BY VALUE WS-LEN", + " SET WS-PTR TO ADDRESS OF WS-CA", + " CALL 'GGCAREA' USING WS-PTR BY VALUE WS-LEN", + " SET ADDRESS OF LK-CA TO WS-PTR", + f" CALL '{program}'" + (" USING LK-CA" if has_commarea else ""), + " CALL 'GGCAOUT' USING LK-CA BY VALUE WS-LEN", " CALL 'GGCEND' USING GG-CICS", " STOP RUN."] # fmt: skip assert all(len(ln) <= 65 for ln in lines), [ln for ln in lines if len(ln) > 65] return "".join(" " + ln + "\n" for ln in lines) @@ -1240,6 +1246,14 @@ def commarea_fields(corpus: Path, case: dict[str, Any]) -> list[dict[str, Any]]: """The COMMAREA layout: the case's (copybook, record) segments laid end to end. A segment in a program's own source (CardDemo's COUSR02C: COPY COCOM01Y then its own 05 items in the same 01) finds the COPY members in the case's copy_dirs.""" + if "commarea" not in case: + raise Unsupported('the case does not describe its COMMAREA: "commarea": {"segments": [...]}, or "commarea": ' + "null for a program that takes none", ["COMMAREA"]) # fmt: skip + if case["commarea"] is None: # #4270: the program takes no COMMAREA (an empty LINKAGE SECTION): EIBCALEN 0 + return [] + if not case["commarea"].get("segments"): + raise Unsupported('"commarea": {"segments": []} describes nothing: "commarea": null for a program that takes ' + "none", ["COMMAREA"]) # fmt: skip out, at = [], 0 for seg in case["commarea"]["segments"]: src = corpus / seg["copybook"] @@ -1250,6 +1264,23 @@ def commarea_fields(corpus: Path, case: dict[str, Any]) -> list[dict[str, Any]]: return out +def commarea_length(sc: dict[str, Any], ca_fields: list[dict[str, Any]]) -> int | None: + """#4270 (oracle_assumptions.md X23): a scenario's stated EIBCALEN, `"commarea_length": N` -- the caller passed + the first N bytes of the COMMAREA record and no more (GenApp's `IF EIBCALEN < 91`, its '98' "COMMAREA too short" + returns). Both sides give the program exactly N bytes: the stub's guard page (GGCAREA) and the det port's + DFHCOMMAREA (CicsTask.withExactCommarea) stop a reference past them, judged up to it. None when not stated.""" + n = sc.get("commarea_length") + if n is None: + return None + whole = max((f["offset"] + f["bytes"] for f in ca_fields), default=0) + if sc.get("commarea") is None or not ca_fields: + raise Unsupported(f"scenario {sc['name']}: a commarea_length with no COMMAREA", ["COMMAREA"]) + if isinstance(n, bool) or not isinstance(n, int) or not 1 <= n <= whole: + raise Unsupported(f"scenario {sc['name']}: commarea_length {n!r} is not a length of the {whole}-byte COMMAREA " + "(1 to its length)", ["COMMAREA"]) # fmt: skip + return n + + def screen_fields(corpus: Path, case: dict[str, Any], map_name: str, side: str) -> list[dict[str, Any]]: scr = case["screens"][map_name] return common.layout_fields(corpus, str(common.case_path(corpus, scr["copybook"])), scr[side]) @@ -1443,7 +1474,15 @@ def run_cobol_cics(case: dict[str, Any], corpus: Path, work: Path, files: list[d f"{f['key_length']}{_cfg_flags(case, f)}\n" for f in files ), encoding="ascii") # fmt: skip if sc.get("commarea") is not None: - (d / "commarea.in").write_bytes(encode_record(ca_fields, sc["commarea"], b"init", enc)) + if not ca_fields: + raise Unsupported(f'scenario {sc["name"]}: a COMMAREA, in a case whose program takes none ("commarea": ' + "null)", ["COMMAREA"]) # fmt: skip + area = encode_record(ca_fields, sc["commarea"], b"init", enc) + calen = commarea_length(sc, ca_fields) + if calen is not None: # #4270 (X23): the stub gives the program exactly those bytes (GGCAREA) + area = area[:calen] + (d / "commarea.exact").write_text(f"{calen}\n", encoding="ascii") + (d / "commarea.in").write_bytes(area) for m, typed in (sc.get("receive") or {}).items(): (d / f"receive_{m}.bin").write_bytes(map_input(screen_fields(corpus, case, m, "input"), typed, enc)) if programs is not None: # the CSD's programs; absent, every program is defined @@ -1503,7 +1542,7 @@ def run_cobol_cics(case: dict[str, Any], corpus: Path, work: Path, files: list[d rc = work / "scenarios" / sc["name"] / "rc" said = work / "scenarios" / sc["name"] / "stdout.txt" if rc.is_file() and rc.read_text().strip() == "98" and said.is_file(): - x6 = x6_writeq_refusal(said.read_text(encoding="latin-1")) + x6 = judged_refusal(said.read_text(encoding="latin-1")) if x6: # owner decision on #4607 (X6): judged up to the refused WRITEQ, derived or not -- what ran before sc["prefix_x6"] = x6 # it counts (its coverage too); its end state is not compared continue @@ -1655,6 +1694,8 @@ def java_ts_as_compared(e: dict[str, Any]) -> dict[str, Any]: X6_WRITEQ = re.compile(r"WRITEQ T[SD] LENGTH > FROM: not modelled") +# #4270 (X23): the stub's guard page after a COMMAREA of a stated length (GGCAREA): a reference past EIBCALEN +X23_PAST = re.compile(r"COMMAREA past EIBCALEN: not modelled") def x6_writeq_refusal(said: str) -> str | None: @@ -1665,6 +1706,13 @@ def x6_writeq_refusal(said: str) -> str | None: return m.group(0) if m else None +def judged_refusal(said: str) -> str | None: + """The stub's stops a task is judged up to: a WRITEQ past FROM (X6) or, in a scenario with a stated COMMAREA + length, a reference past EIBCALEN (#4270, X23) -- its line, or None for any other "not modelled" stop.""" + m = X23_PAST.search(said) + return x6_writeq_refusal(said) or (m.group(0) if m else None) + + def outputs(out: Path, case: dict[str, Any], corpus: Path, ca_fields: list[dict[str, Any]]) -> dict[str, Any]: """A task's outputs from the stub's log: `events` (every command, in order), `screens` ([{map, fields}] per SEND MAP, data fields only: O -> ), `text` (SEND TEXT), @@ -1700,6 +1748,9 @@ def outputs(out: Path, case: dict[str, Any], corpus: Path, ca_fields: list[dict[ res["text"].append(f"" if text is None else text.rstrip(" \x00")) elif verb in ("RETURN", "XCTL") and int(kv.get("level", "1")) <= 1: # the task's own (a LINK level's: no) key = "transid" if verb == "RETURN" else "program" + if data and not ca_fields: # #4270: nothing to read it by -- refused, never compared as empty + raise Unsupported(f"{verb} with a COMMAREA, in a case that describes none (\"commarea\": null)", + ["COMMAREA"]) # fmt: skip ca = decode_record(data, ca_fields, enc) if data else None res[verb.lower()] = {key: kv.get(key, ""), "commarea": ca} elif verb == "ABEND": @@ -1887,10 +1938,11 @@ def _generated_class(src: Path, pattern: str) -> str: raise RuntimeError(f"no generated class matches {pattern!r}") -def commarea_class(case: dict[str, Any], src: Path, svc_file: Path) -> str: +def commarea_class(case: dict[str, Any], src: Path, svc_file: Path) -> str | None: """The COMMAREA DTO the task carries: the one the service's handleTransaction takes -- or, for a program that receives none and only builds one (CardDemo's sign-on, COSGN00C: it tests EIBCALEN, then XCTLs with - CARDDEMO-COMMAREA), the generated DTO of the case's first COMMAREA record.""" + CARDDEMO-COMMAREA), the generated DTO of the case's first COMMAREA record. None for a case whose program takes no + COMMAREA at all (`"commarea": null`, #4270: the async credit-card services, whose det service is handleLink()).""" from gitgalaxy.tools.cobol_to_java.cobol_to_java_names import java_class_base svc_text = svc_file.read_text(encoding="utf-8") @@ -1899,6 +1951,8 @@ def commarea_class(case: dict[str, Any], src: Path, svc_file: Path) -> str: ) # a LINKed program (CBSA): its contract COMMAREA DTO if m: return m.group(1) + if "commarea" in case and case["commarea"] is None: # #4270: a program that takes none (handleLink()): no DTO + return None record = case["commarea"]["segments"][0]["record"] name = java_class_base(record) if not any(src.rglob(f"{name}.java")): @@ -2026,6 +2080,9 @@ def commarea_class(case: dict[str, Any], src: Path, svc_file: Path) -> str: void start(Object service) { if (linkedEntry) { Method m = method(service, "handleLink", 1); + if (m == null && commarea == null) { // #4270: a program that takes no COMMAREA: handleLink() + m = method(service, "handleLink", 0); + } if (m == null) { runTask(service); return; @@ -2037,7 +2094,7 @@ def commarea_class(case: dict[str, Any], src: Path, svc_file: Path) -> str: linked = scenario; linkedProgram = program; entry(program, "handleLink"); - Object got = call(m, service, commarea); + Object got = m.getParameterCount() == 0 ? call(m, service) : call(m, service, commarea); if (linked != null) { throw new FacadeRefused("handleLink of " + program + " did not run its task in the region"); } @@ -2199,11 +2256,19 @@ def cics_equivalence_test(case: dict[str, Any], src: Path, files: list[dict[str, svc = ej._service_class(case["program"]) var = svc[0].lower() + svc[1:] ca = commarea_class(case, src, next(src.rglob(f"{svc}.java"))) + commarea_java = ("null" if ca is None else 'sc.get("commarea").isNull() ? null\n : ' + f'json.treeToValue(sc.get("commarea"), {pkg}.dto.contract.{ca}.class)') # fmt: skip screens = {m: _generated_class(src, rf'String MAP = "{m}";') for m in case["screens"]} # The estate has the CICS exception package only when some program throws or handles one (UowForge): IBM DBB # MortgageApplication's do not, so nothing there can throw CicsAbendException and the test catches none. has_abend = any(src.rglob("exception/CicsAbendException.java")) abend_import = f"import {pkg}.exception.CicsAbendException;\n" if has_abend else "" + # #4270: an estate with no BMS map (the async credit-card example) has no screen view models to convert + has_screens = any(src.rglob("dto/screen/ScreenModel.java")) + screen_import = f"import {pkg}.dto.screen.ScreenModel;\n" if has_screens else "" + screen_copy = (" if (copy.get(\"screen\") instanceof ScreenModel s) {\n" + " copy.put(\"screen\", s.screenValues());\n" + " }\n") if has_screens else "" # fmt: skip by_base = {f["base"]: f for f in files} region = case.get("region") or {} region_java = f'"{region["applid"]}", "{region["sysid"]}"' if region.get("applid") else "null, null" @@ -2271,8 +2336,7 @@ def cics_equivalence_test(case: dict[str, Any], src: Path, files: list[dict[str, import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import {pkg}.cics.CicsTask; -import {pkg}.dto.screen.ScreenModel; -{abend_import}import java.io.IOException; +{screen_import}{abend_import}import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -2335,12 +2399,13 @@ class EquivalenceRunTest {{ // each task starts from the files as loaded, and what it leaves is compared (file updates) {chr(10).join(loads)} {db2_reset} - Object commarea = sc.get("commarea").isNull() ? null - : json.treeToValue(sc.get("commarea"), {pkg}.dto.contract.{ca}.class); + Object commarea = {commarea_java}; + // #4270 (X23): a stated COMMAREA length (EIBCALEN) shorter than the record: the area is that long + Integer calen = sc.path("commarea_length").isInt() ? Integer.valueOf(sc.get("commarea_length").asInt()) : null; Map received = new LinkedHashMap<>(); JsonNode r = sc.get("receive"); {chr(10).join(recv)} - CicsTask task = new CicsTask("{case["transid"]}", sc.get("aid").asText(), commarea, received) + CicsTask task = new CicsTask("{case["transid"]}", sc.get("aid").asText(), commarea, calen, received) .withClock(java.time.LocalDateTime.parse("{ej._clock(case)}")) // EIBTIME / ASKTIME: the case's clock .withRegion({region_java}) // ASSIGN APPLID / SYSID .withProgram("{case["program"]}") // the task's first program (a LINK's INVOKINGPROG) @@ -2354,6 +2419,9 @@ class EquivalenceRunTest {{ t.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW); t.executeWithoutResult(s -> change.run()); }}); + if (calen != null) {{ + task.withExactCommarea(); + }} FacadeRegion region = new FacadeRegion(task, commarea, "{case["program"]}", {"true" if case.get("linked") else "false"}); {csd_java} List faults = new ArrayList<>(); // #4023 follow-up: the scenario's injected conditions @@ -2413,10 +2481,7 @@ class EquivalenceRunTest {{ List> events = new ArrayList<>(); for (Map e : task.events()) {{ Map copy = new LinkedHashMap<>(e); - if (copy.get("screen") instanceof ScreenModel s) {{ - copy.put("screen", s.screenValues()); - }} - events.add(copy); +{screen_copy} events.add(copy); }} if (commarea != null) {{ // a LINKed program's result: the COMMAREA it leaves (equivalence_cics.linked_result) Map left = new LinkedHashMap<>(); @@ -2518,7 +2583,9 @@ def run_java_cics(case: dict[str, Any], corpus: Path, work: Path, cobol_work: Pa svc = ej._service_class(case["program"]) svc_file = next(src.rglob(f"{svc}.java")) ca_cls = commarea_class(case, src, svc_file) - shape = dto_shape(src, ca_cls, svc_file) # #4011: the class the service imports, not any of that name + shape = ( + dto_shape(src, ca_cls, svc_file) if ca_cls else {} + ) # #4011: the class the service imports, not any of that name seams = sql_seam_programs(case, src) # #4173 x #4188: whose Java can take an injected SQL fault test = project / "src/test/java" / ej.PKG_DIR / "EquivalenceRunTest.java" test.write_text( @@ -2553,7 +2620,7 @@ def run_java_cics(case: dict[str, Any], corpus: Path, work: Path, cobol_work: Pa scenarios.append({"name": sc["name"], "aid": sc.get("aid", "DFHENTER").removeprefix("DFH"), "commarea": ca, "receive": receive, "faults": fault_lines(sc), "sql_faults": sc.get("sql_plan", []), "sql_unjudged": sql_unjudged(sc.get("sql_plan", []), seams), - "derived": bool(sc.get("derived")), + "derived": bool(sc.get("derived")), "commarea_length": commarea_length(sc, ca_fields), "counters": _counters(case, sc), "taskn": task_number(case, sc)}) # fmt: skip (inputs / "scenarios.json").write_text(json.dumps(scenarios, indent=1), encoding="utf-8") props = ej.data_charset_arg(case) @@ -3016,6 +3083,14 @@ def _to_link(events: list[dict[str, Any]], program: str) -> list[dict[str, Any]] X6_JUDGED = ("the refused WRITEQ (X6: its LENGTH runs past FROM; not settled on z/OS, #4050) -- what the task did " "before it is compared, its end state (files, tables, the COMMAREA it leaves) is not") # fmt: skip +X23_JUDGED = ("the refused reference past EIBCALEN (X23: a COMMAREA of a stated length; z/OS shows whatever storage " + "follows it) -- what the task did before it is compared, its end state (files, tables, the COMMAREA it " + "leaves) is not") # fmt: skip + + +def judged_to(x6: dict[str, Any]) -> str: + """The report's `judged_to` for a task stopped at a refusal (x6_judged): X6's words, or X23's.""" + return X23_JUDGED if x6.get("assumes") == "X23" else X6_JUDGED def x6_judged(sc: dict[str, Any], java_out: Path, cev: list[dict[str, Any]], @@ -3029,7 +3104,13 @@ def x6_judged(sc: dict[str, Any], java_out: Path, cev: list[dict[str, Any]], if not sc.get("prefix_x6") and java is None: return cev, jev, None left = [e for e in cev if e.get("event") != "COMMAREA"], [e for e in jev if e.get("event") != "COMMAREA"] - return left[0], left[1], {"cobol": sc.get("prefix_x6"), "java": java, "ok": bool(sc.get("prefix_x6") and java)} + cobol = sc.get("prefix_x6") + + def kind(said: str | None) -> str | None: # #4270: which refusal -- both sides must have stopped at the same one + return None if not said else "X23" if "EIBCALEN" in said else "X6" + + return left[0], left[1], {"cobol": cobol, "java": java, "ok": bool(cobol and java) and kind(cobol) == kind(java), + "assumes": kind(cobol) or kind(java)} # fmt: skip def _fired(log: Path) -> list[str]: @@ -3068,7 +3149,7 @@ def judge_facade(case: dict[str, Any], corpus: Path, files: list[dict[str, Any]] "entries": facade["entries"].get(name, [])} # fmt: skip ok = d["equal"] == d["events"] if x6: - o["judged_to"], o["x6"] = X6_JUDGED, x6 + o["judged_to"], o["x6"] = judged_to(x6), x6 ok &= x6["ok"] why = facade["refused"].get(name) if why: @@ -3188,10 +3269,10 @@ def run_case(case: dict[str, Any], corpus: Path, work: Path, port: bool = True, if sc.get("derived"): report["outputs"][name]["sql_faults"] = sc["sql_plan"] if x6: - report["outputs"][name]["judged_to"], report["outputs"][name]["x6"] = X6_JUDGED, x6 + report["outputs"][name]["judged_to"], report["outputs"][name]["x6"] = judged_to(x6), x6 if not x6["ok"]: ok = False - print(f"{case['program']} {name}: only one side refused the WRITEQ past FROM (X6): {x6}") + print(f"{case['program']} {name}: only one side refused ({x6['assumes']}): {x6}") if sc.get("prefix_link"): report["outputs"][name]["judged_to"] = ( f"LINK PROGRAM({sc['prefix_link']}) (not run: its end state is not compared)" diff --git a/tests/tools/evidence.py b/tests/tools/evidence.py index 71dce61830..a84759ffd8 100644 --- a/tests/tools/evidence.py +++ b/tests/tools/evidence.py @@ -306,8 +306,8 @@ def proof_section(t: Target, report: dict[str, Any], digest: str) -> dict[str, A outputs[name] = {"equal": o.get("equal"), "records": o.get("records", o.get("events"))} if o.get("judged_to"): # #4173 / #4607 (X6): a task compared only up to a LINK not run or a refused WRITEQ outputs[name]["judged_to"] = o["judged_to"] - if o.get("x6"): - outputs[name]["assumes"] = "X6" + if o.get("x6"): # #4270: or X23, a reference past a stated EIBCALEN + outputs[name]["assumes"] = o["x6"].get("assumes", "X6") envs = [e["name"] for e in report.get("environments") or []] rc = report.get("return_code") or {} sysout = report.get("sysout") @@ -767,13 +767,19 @@ def render_page(t: Target, rec: dict[str, Any]) -> str: else f"person ({pv.get('author') or 'unknown'})") # fmt: skip lines.append(f"| written by | {who} |") lines += ["", "## Proof outputs", "", "| output | equal | compared |", "|---|---|---|"] - lines += [f"| {k} | {v['equal']} | {v['records']}{' (judged up to the refusal: X6)' if v.get('assumes') == 'X6' else ''} |" - for k, v in sorted((p.get("outputs") or {}).items())] # fmt: skip + for k, v in sorted((p.get("outputs") or {}).items()): + refusal = f" (judged up to the refusal: {v['assumes']})" if v.get("assumes") else "" + lines.append(f"| {k} | {v['equal']} | {v['records']}{refusal} |") x6 = sorted(k for k, v in (p.get("outputs") or {}).items() if v.get("assumes") == "X6") if x6: # owner decision on #4607: the proof states X6 as an assumption of these tasks lines += ["", f"Assumes oracle_assumptions.md X6 for {len(x6)} task(s) ({', '.join(x6)}): each is compared up " "to a WRITEQ whose LENGTH runs past its FROM area, refused on both sides; what z/OS writes there and " "what the task does after it are not claimed (not settled on z/OS, #4050)."] # fmt: skip + x22 = sorted(k for k, v in (p.get("outputs") or {}).items() if v.get("assumes") == "X23") + if x22: # #4270: a task given a COMMAREA of a stated length that referenced past it + lines += ["", f"Assumes oracle_assumptions.md X23 for {len(x22)} task(s) ({', '.join(x22)}): each is given a " + "COMMAREA shorter than its record and compared up to a reference past EIBCALEN, refused on both " + "sides; what z/OS shows there (the storage that follows the area) is not claimed."] # fmt: skip fc = p.get("facade") if fc: # #4449 lines += ["", "## Through the deployed entry points (java-facade, #4449)", "", From 3ed15334296db59e036705f08f4e29b710835b9a Mon Sep 17 00:00:00 2001 From: squid-protocol Date: Wed, 7 Oct 2026 16:07:25 -0400 Subject: [PATCH 2/2] equivalence: Db2 CICS cases re-proven under the harness change; ledger entries (#4270) Co-Authored-By: Claude Opus 5.5 --- tests/equivalence/det_sweep_coverage.json | 237 +++++++++++++++++++++- 1 file changed, 229 insertions(+), 8 deletions(-) diff --git a/tests/equivalence/det_sweep_coverage.json b/tests/equivalence/det_sweep_coverage.json index 41177ffebd..7debed5745 100644 --- a/tests/equivalence/det_sweep_coverage.json +++ b/tests/equivalence/det_sweep_coverage.json @@ -15,7 +15,7 @@ "inputs": { "case": "8715eb31cb1fe3d43aec7c2ce9de9d3b6e643b65113edc51fb979c8d39d43e21", "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -32,7 +32,7 @@ "inputs": { "case": "3f15c0e7364770e323f6e9c65487c146dbfba37ff81f38d42b9aebc930125c49", "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -49,7 +49,7 @@ "inputs": { "case": "6f6238b16a7d8e202086190913f64017584104833d6a343623c615faa618d8ac", "corpus": "a98b9cfe46b5f45db9305f0a997047218e09c7c36422ec0849f6af7af5dd5353", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -172,6 +172,40 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "carddemo-cotrtlic": { + "scenarios": 27, + "paragraphs": { + "covered": 56, + "live": 59 + }, + "branches": { + "covered": 165, + "total": 230 + }, + "inputs": { + "case": "0a2e4bde071d15909a45ecae9770b8a578519ddeca50aef5846966740ed76a36", + "corpus": "d0c4803872ec126eb56a170212f76207beff5ab8c2b6507dd6df4a084cdeb919", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "carddemo-cotrtupc": { + "scenarios": 38, + "paragraphs": { + "covered": 62, + "live": 63 + }, + "branches": { + "covered": 126, + "total": 166 + }, + "inputs": { + "case": "c9136c4e3c7315197bc090d622aeac23563ff7303bb6e7789052a3a25b51049c", + "corpus": "d0c4803872ec126eb56a170212f76207beff5ab8c2b6507dd6df4a084cdeb919", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "carddemo-menu": { "scenarios": 16, "paragraphs": { @@ -359,6 +393,108 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "cbsa-custctrl": { + "scenarios": 6, + "paragraphs": { + "covered": 7, + "live": 7 + }, + "branches": { + "covered": 3, + "total": 4 + }, + "inputs": { + "case": "5abdcd4d81aa4bec7b6474e7659207b876d49926c72fd8262536ac45df3dd604", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "cbsa-dbcrfun": { + "scenarios": 25, + "paragraphs": { + "covered": 16, + "live": 22 + }, + "branches": { + "covered": 25, + "total": 35 + }, + "inputs": { + "case": "01ea326724a638ab18518bcd9da7b7701545b804756ea1e33a5204248ebdd1ae", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "cbsa-delacc": { + "scenarios": 14, + "paragraphs": { + "covered": 19, + "live": 19 + }, + "branches": { + "covered": 13, + "total": 14 + }, + "inputs": { + "case": "2049daea2586d2211c74ebfb5831a707bb965c4de0ee7bbf6e08f1e30b891c4a", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "cbsa-inqacc": { + "scenarios": 17, + "paragraphs": { + "covered": 22, + "live": 25 + }, + "branches": { + "covered": 15, + "total": 25 + }, + "inputs": { + "case": "a39d307ace7093196d4217da06487c426d80acc673d572f5697f7a0c8d23af97", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "cbsa-updacc": { + "scenarios": 12, + "paragraphs": { + "covered": 7, + "live": 7 + }, + "branches": { + "covered": 6, + "total": 6 + }, + "inputs": { + "case": "d0e0d5193ee3b4c8761dd37a70edb3fe5e1d78c5a785ea583e685637a21047f3", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, + "cbsa-updacc-generated": { + "scenarios": 42, + "paragraphs": { + "covered": 7, + "live": 7 + }, + "branches": { + "covered": 6, + "total": 6 + }, + "inputs": { + "case": "28a4504359e22c2086f18e990f009dc5165131b6a7a0e270bb16f6325ec3c809", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "cbsa-updcust": { "scenarios": 23, "paragraphs": { @@ -376,6 +512,23 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "cbsa-xfrfun": { + "scenarios": 27, + "paragraphs": { + "covered": 27, + "live": 28 + }, + "branches": { + "covered": 46, + "total": 81 + }, + "inputs": { + "case": "7ffdb8e87cf7a2805e24f593a534b1869317be541532206beca022599b11b056", + "corpus": "7a4c86e6d6688f1516ec00cbd81941457457c861bf900e2b3a7f269c6bf9058b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "genapp-lgacdb01": { "scenarios": 9, "paragraphs": { @@ -389,7 +542,7 @@ "inputs": { "case": "253e59e79e236523d89d2d0ab74c4347955c262c85ae2e7f658230152c319c13", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -406,7 +559,7 @@ "inputs": { "case": "a3ae0bfb1c47fd295bc6814406a2d998597903c43943999916a5aefc93eebb76", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -427,6 +580,23 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "genapp-lgapdb01": { + "scenarios": 9, + "paragraphs": { + "covered": 6, + "live": 7 + }, + "branches": { + "covered": 17, + "total": 31 + }, + "inputs": { + "case": "aecc23b4f7c314678ce7199f8545341463d983410250042c3707999ebf57d568", + "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "genapp-lgapvs01": { "scenarios": 8, "paragraphs": { @@ -457,7 +627,7 @@ "inputs": { "case": "551cd3e514845511da462dd0e64d6542d64ae2b98d2756cd8d31e2f4c9f0c373", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -478,6 +648,23 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "genapp-lgicdb01": { + "scenarios": 13, + "paragraphs": { + "covered": 3, + "live": 4 + }, + "branches": { + "covered": 4, + "total": 12 + }, + "inputs": { + "case": "006fc6e96c5aa78ac54aa15f16be202c12f90beb19cc4118a6bf54ee6c29fd3a", + "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "genapp-lgipdb01": { "scenarios": 55, "paragraphs": { @@ -491,7 +678,7 @@ "inputs": { "case": "ed0171d8c5324bfa7a5abd7a11613602d4192660eb6cb0045e52558a87068431", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -508,7 +695,7 @@ "inputs": { "case": "d690be3b361358cb5c8ce9e490a5281f4b2aa164b023d5c18c09fec94fc4a651", "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", - "harness": "9179ad80f3229cf208f51ea99ffa3ccff4e69978f8058be12afbbb09422dd88b", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" } }, @@ -529,6 +716,23 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "genapp-lgupdb01": { + "scenarios": 12, + "paragraphs": { + "covered": 9, + "live": 9 + }, + "branches": { + "covered": 26, + "total": 38 + }, + "inputs": { + "case": "74984f1fdb517f9a636a73d3f6c0f39a47c059c5d0f9d5042e80af4e0a808992", + "corpus": "c5e4d52071fad96bed24e9ba06f30417340bfc5e998e7c1cd5f2383be73d6ff6", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "genapp-lgupvs01": { "scenarios": 6, "paragraphs": { @@ -546,6 +750,23 @@ "oracle": "c01338417b189840c3da3fcf109428b6c3c5db987111699423026a5a2aa260eb" } }, + "mortgage-cmort": { + "scenarios": 9, + "paragraphs": { + "covered": 3, + "live": 6 + }, + "branches": { + "covered": 15, + "total": 21 + }, + "inputs": { + "case": "bd85a34c697e713b92f53b2de2fd71639c0622ffcc2b9f6fe4d2f02ed1495c9d", + "corpus": "1143e0fef7c983b7ade463d33d49481f5572d79e8f95593a2219fdb494a1e7f2", + "harness": "a6e5a39c361f70598664122a175d8b46f1fc36a3d21edea8fd0faf714d09d617", + "oracle": "fd20c9a4f4f68e6dccfae93669be4c30ec66eacd41330b329e70c039477b886a" + } + }, "mortgage-mlist": { "scenarios": 9, "paragraphs": {