diff --git a/tests/cobol_mainframe/test_det_bytes_4690.py b/tests/cobol_mainframe/test_det_bytes_4690.py index de51d1f474..fd1fb9b3be 100644 --- a/tests/cobol_mainframe/test_det_bytes_4690.py +++ b/tests/cobol_mainframe/test_det_bytes_4690.py @@ -8,7 +8,7 @@ import shutil import pytest -from test_det_programs import _cobol, _java, _java_run, program +from test_det_programs import ROOT, _cobol, _java, _java_run, _java_run_batch, program # noqa: F401 E2E = pytest.mark.skipif(os.environ.get("EQUIVALENCE_E2E") != "1" or not shutil.which("docker") or _java() is None, reason="needs Docker and a JDK 17 (JAVA_HOME / JDK_17)") # fmt: skip @@ -53,3 +53,36 @@ def test_every_byte_hex_literal_and_display_equal_gnucobols(mode, tmp_path): assert isinstance(want, bytes) and want assert b"\xc3\xbf" not in got assert got == want + + +def high_byte_program(name: str) -> str: + """One MOVE X'hh' TO R and DISPLAY per byte 0x80-0xFF (all above X'7F').""" + proc: list[str] = [] + for b in range(0x80, 0x100): + proc += [f"MOVE X'{b:02X}' TO R", "DISPLAY R"] + return program(name, ["01 R PIC X."], proc) + + +@E2E +@pytest.mark.parametrize( + "charset", + [ + "ISO-8859-1", + *( + pytest.param(c, marks=pytest.mark.xfail(strict=True, reason="#4698: hex-literal bytes do not round-trip")) + for c in ("IBM037", "IBM1047", "windows-1252") + ), + ], +) +@pytest.mark.parametrize("mode", ["bytes", "typed", "groups"]) +def test_batch_sysout_high_bytes_equal_gnucobols(charset, mode, tmp_path): + """#4697: the batch Sysout (not the standalone one) writes the record charset's bytes for DISPLAY of X'80'..X'FF', + byte for byte as cobc does, under latin-1 and under single-byte EBCDIC record charsets.""" + pytest.importorskip("tree_sitter_language_pack") + src = high_byte_program("HIBATCH") + cob = tmp_path / "cobol" + cob.mkdir() + want = _cobol(src, cob, raw=True) + got = _java_run_batch("HIBATCH", src, tmp_path, [], mode != "bytes", mode == "groups", raw=True, charset=charset) + assert isinstance(got, bytes) and want + assert got == want, f"{charset}: java {got[:40]!r} != cobol {want[:40]!r}" diff --git a/tests/cobol_mainframe/test_det_programs.py b/tests/cobol_mainframe/test_det_programs.py index 1d6ce85212..fcb7e47300 100644 --- a/tests/cobol_mainframe/test_det_programs.py +++ b/tests/cobol_mainframe/test_det_programs.py @@ -1301,21 +1301,30 @@ def _java_run( check=True).stdout # fmt: skip -def _batch_package(srcdir: Path) -> None: +def _batch_package(srcdir: Path, charset: str = "ISO-8859-1") -> None: """The generated project's batch package (CobolFiles, DatasetResolver, Dd, MainframeClock, Sysout, CobolAbend) from the batch forge's own templates, without Spring: what a det port with files runs on.""" from gitgalaxy.tools.cobol_to_java.cobol_to_java_batch_forge import _RUNTIME for cls in ("CobolFiles", "DatasetResolver", "Dd", "MainframeClock", "Sysout", "CobolAbend"): text = _RUNTIME[cls].replace("{pkg}", f"{PKG}.batch").replace("{zone}", "UTC") - text = text.replace("{record_charset}", "ISO-8859-1") + text = text.replace("{record_charset}", charset) text = re.sub(r"^import org\.springframework\..*\n|^@Component\n", "", text, flags=re.M) text = re.sub(r'@Value\("(?:[^"\\]|\\.)*"\)\s*', "", text) (srcdir / PKG / "batch" / f"{cls}.java").parent.mkdir(parents=True, exist_ok=True) (srcdir / PKG / "batch" / f"{cls}.java").write_text(text) -def _java_run_batch(name: str, src: str, work: Path, dds: list[str], typed: bool = False, groups: bool = False) -> str: +def _java_run_batch( + name: str, + src: str, + work: Path, + dds: list[str], + typed: bool = False, + groups: bool = False, + raw: bool = False, + charset: str = "ISO-8859-1", +) -> str: """As _java_run, on the batch runtime (DetFiles): runBatch with one DD per dataset, each a file of its name in a datasets directory.""" from gitgalaxy.tools.cobol_to_java.det import program as P @@ -1333,11 +1342,11 @@ def _java_run_batch(name: str, src: str, work: Path, dds: list[str], typed: bool for rel, text in [(f"service/{r.service}.java", java), *runtime.items()]: (srcdir / PKG / rel).parent.mkdir(parents=True, exist_ok=True) (srcdir / PKG / rel).write_text(text) - _batch_package(srcdir) + _batch_package(srcdir, charset) rec = srcdir / PKG / "entity/vsam/CobolRecords.java" rec.parent.mkdir(parents=True, exist_ok=True) rec.write_text(f"package {PKG}.entity.vsam;\npublic final class CobolRecords {{\n public static java.nio.charset." - "Charset charset() {\n return java.nio.charset.StandardCharsets.ISO_8859_1;\n }\n}\n") # fmt: skip + "Charset charset() {\n return java.nio.charset.Charset.forName(\"" + charset + "\");\n }\n}\n") # fmt: skip data = work / "datasets" data.mkdir() b = f"{PKG}.batch" @@ -1350,7 +1359,7 @@ def _java_run_batch(name: str, src: str, work: Path, dds: list[str], typed: bool jdk = _java() files = [str(f) for f in srcdir.rglob("*.java")] subprocess.run([str(jdk / "javac"), "-nowarn", "-d", str(work / "classes"), *files], check=True) # noqa: S603 - return subprocess.run([str(jdk / "java"), "-cp", str(work / "classes"), "Main"], capture_output=True, text=True, # noqa: S603 + return subprocess.run([str(jdk / "java"), "-cp", str(work / "classes"), "Main"], capture_output=True, text=not raw, # noqa: S603 check=True).stdout # fmt: skip