Files
cobol-java-v3/SETUP.md
T

487 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# COBOL Test Data Generator — 环境搭建与运行指南
## 1. 系统概述
COBOL 测试数据生成器(cobol-java-v3)是一个 Python 工具链,用于解析 COBOL 程序、提取控制流结构、生成覆盖所有分支的测试数据,并输出为固定的 flat file 格式供 GnuCOBOL 编译运行。
系统支持两条平行的测试管道:**非 DB 管道**flat file I-O 程序 → cobc 编译运行 → 二进制比对)和 **DB 管道**EXEC SQL 程序 → gixpp ESQL 预处理 → cobc + libgixsql 编译运行 → SQLite 表比对),自动根据源码中是否包含 `EXEC SQL` 路由到对应管道。
### 核心能力
| 能力 | 说明 |
|------|------|
| 解析 COBOL DATA DIVISION | Lark 语法 (Earley parser) → 字段定义 |
| 解析 COBOL PROCEDURE DIVISION | 行级状态机 → 决策点树 + EXEC SQL 抽取 |
| 分支覆盖数据生成 | 每决策点生成 True/False 路径 → 记录 |
| Flat file + SQLite DB 双输出 | 非 DB 程序→二进制 flat fileDB 程序→flat file + SQLite 表行 |
| GnuCOBOL 编译运行 (非 DB) | 测试数据 → `cobc` 编译 → 运行 → 二进制比对 |
| gixsql 编译运行 (DB) | `gixpp` ESQL 预处理 → `cobc -l gixsql` 编译 → SQLite 运行 → 表比对 |
| 覆盖率报告 (gcov) | `--gcov` 模式下编译带 `-g --coverage`,合并后生成 HTML 报告 |
---
## 2. 必要条件
### 2.1 硬件要求
| 项目 | 最低 | 推荐 |
|------|------|------|
| CPU | 2 cores | 4+ cores |
| 内存 | 4 GB | 8 GB |
| 磁盘 | 500 MB | 2 GB |
| OS | Windows 10/11 64-bit | Windows 11 |
### 2.2 软件要求
| 软件 | 版本 | 用途 |
|------|------|------|
| **Python** | 3.12+ | 运行测试数据生成器 |
| **GnuCOBOL (cobc)** | 3.2.0 (GC32-BDB-SP1) | 编译 COBOL 程序 & 运行时验证(DB 管道需要含 SQLite 支持的版本) |
| **gixsql** | 已 vendored (`gixsql/`) | ESQL 预处理器 (`gixpp.exe`) + 运行时 DLL (`libgixsql.dll`)DB 管道必备 |
| **Git** | 任意 | 拉取代码 |
### 2.3 Python 依赖
```
lark>=1.1.0 # Lark Earley parser (DATA DIVISION 解析)
pathlib>=1.0.1 # 路径处理
pyyaml>=6.0 # YAML 配置加载(DB 管道:per-program schema
```
安装命令:
```bash
pip install lark pathlib pyyaml
```
### 2.4 GnuCOBOL 安装
GnuCOBOL 3.2.0 (OpenCOBOL) 需要单独安装。
**下载**:
- GnuCOBOL 3.2 Windows 二进制包
- 推荐: GC32-BDB-SP1 版本(含 DB2/SQLite 支持)
**安装后确认**:
```bash
cobc --version
# 输出示例: cobc (GnuCOBOL) 3.2.0
```
**环境变量**
```bash
# cobc 需要在 PATH 中
# 典型路径: C:\GnuCOBOL\bin
# 或自定义安装路径
# COB_LIBRARY_PATH — 运行时 DLL 搜索路径
# 非 DB 管道:子程序 DLL 目录(如 cobol-tna-system\bin
# DB 管道:必须包含 gixsql\lib\libgixsql.dll, libgcc_s_dw2-1.dll 等)
# 如: set COB_LIBRARY_PATH=D:\cobol-java\cobol-java-v3\gixsql\lib
# GIXSQL_DB_PATH — DB 管道 SQLite 数据库路径(默认 C:\Temp\gix\
# 如: set GIXSQL_DB_PATH=C:\Temp\gix
```
---
## 3. 环境搭建步骤
### 3.1 安装 Python 3.12+
```bash
# 下载: https://www.python.org/downloads/
# 安装时勾选 "Add Python to PATH"
python --version
# Python 3.12.x
pip install lark pathlib pyyaml
```
### 3.2 安装 GnuCOBOL 3.2
1. 下载 GC32-BDB-SP1 包
2. 解压到 `D:\360安全浏览器下载\GC32-BDB-SP1-rename-7z-to-exe\`
3.`bin\` 子目录添加到系统 PATH
4. 验证:
```bash
cobc --version
# cobc (GnuCOBOL) 3.2.0
```
### 3.3 克隆代码
```bash
cd D:\
git clone https://gittea.dev/hangshuo652/cobol-java-v3.git
# 或从已有仓库拉取
cd D:\cobol-java\cobol-java-v3
git pull
```
### 3.4 验证安装
```bash
cd D:\cobol-java\cobol-java-v3
python -c "from cobol_testgen import extract_structure; print('OK')"
# 输出: OK
```
---
## 4. 目录结构
```
cobol-java-v3/
├── cobol_testgen/ # 核心代码
│ ├── __init__.py # 公开 API (extract_structure, generate_data) + DB 自动路由
│ ├── read.py # 预处理器 + DATA DIVISION 解析 + EXEC SQL INCLUDE 解决
│ ├── core.py # 旧 PROCEDURE DIVISION 解析器 + EXEC SQL 抽取
│ ├── cond.py # 条件解析器
│ ├── coverage.py # 覆盖率统计 (+ gcov HTML 报告)
│ ├── design_mcdc.py # 线性路径枚举 (O(N) 替代 O(2^N))
│ ├── pipeline_bridge.py # 新旧解析器桥接层
│ ├── procedure_parser.py # 新 PROCEDURE DIVISION 解析器
│ ├── flatfile.py # Flat file 写入器
│ ├── design.py # 值生成 + 约束应用
│ ├── to_sql.py # SQL 约束解析 + DB 行生成 (DB 管道)
│ ├── models.py # 数据模型 (BrSeq, BrIf, BrEval...)
│ ├── grammar.lark # DATA DIVISION Lark 语法
│ └── procedure_grammar.lark # PROCEDURE DIVISION Lark 语法 (实验性)
├── orchestrator_db.py # DB 6 步管道编排 (GixsqlOrchestrator)
├── runners/ # 编译运行引擎
│ ├── cobol_runner.py # 标准 GnuCOBOL runner(非 DB
│ ├── gixsql_runner.py # gixpp + cobc runnerDB
│ ├── native_java_runner.py # 原生 Java runner
│ ├── spark_java_runner.py # Spark Java runner
│ ├── data_writer.py # 测试数据写入器
│ └── runner.py # 基类
├── config/
│ ├── program_schema.py # DB 程序 schema 加载器
│ └── programs/ # per-program YAML 配置(6 个 DB 程序)
│ ├── KIN02UPD.yaml
│ ├── KIN03EXP.yaml
│ ├── KIN06CLD.yaml
│ ├── KIN08DBU.yaml
│ ├── KIN09CSV.yaml
│ └── ZAN06UPD.yaml
├── gixsql/ # 已 vendored ESQL 工具链
│ ├── bin/gixpp.exe # ESQL 预处理器
│ └── lib/*.dll # 运行时 (libgixsql, libgixsql-sqlite, libgcc 等)
├── test-data/ # 测试套件
│ ├── s15_coverage_verification.py # 基础覆盖率验证 (8种控制结构)
│ ├── s19_final_bridge_test.py # 桥接器验证
│ ├── s21_cond_fix_verify.py # 条件解析验证
│ ├── s25_per_program_report.py # 每程序详细报告
│ ├── s26_regression_check.py # 回归检查
│ └── s30_db_e2e.py # DB 端到端测试 (ZAN06UPD)
├── diagnose_db2.py # DB 全流程诊断脚本
├── diagnose_kind8dbrun.py # DB 单步诊断 (仅编译运行)
├── SETUP.md # 本文件
└── docs/ # 设计文档
└── runtime/ # 运行产物(测试数据、输入文件、覆盖率报告,已 gitignore)
```
---
## 5. 运行测试
### 5.1 快速验证(10 秒)
```bash
cd D:\cobol-java\cobol-java-v3
python test-data/s15_coverage_verification.py
```
期望输出:
```
S15: 17 PASS / 0 FAIL
```
### 5.2 完整 43 程序覆盖率报告(2-3 分钟)
```bash
python test-data/s25_per_program_report.py
```
期望输出末尾:
```
100%: 43 programs
TOTAL 3178 3178 100%
```
### 5.3 回归快速检查(2 分钟)
```bash
python test-data/s26_regression_check.py
```
期望输出:
```
Total: 3178/3178 = 100.00%
ALL 43/43 AT 100% — NO REGRESSIONS
```
### 5.4 指定 COPYBOOK 目录
如果 COBOL 程序依赖 COPYBOOK,需要在调用 `generate_data` 时指定 `copybook_dirs`
```python
from cobol_testgen import extract_structure, generate_data
src = open("program.cbl", encoding="utf-8").read()
st = extract_structure(src)
recs = generate_data(src, st, copybook_dirs=["path/to/copybooks"])
```
### 5.5 DB 端到端测试(5 分钟)
DB 管道需要使用 `cobol-tna-system` 同级项目中的 DB 程序(含 `EXEC SQL` 的 COBOL 源码):
```bash
# 1. 确认同级目录存在 cobol-tna-system
dir ..\cobol-tna-system
# 2. 设置环境变量
set COB_LIBRARY_PATH=<proj_dir>\gixsql\lib
mkdir C:\Temp\gix 2>nul
set GIXSQL_DB_PATH=C:\Temp\gix
# 3. 运行 DB 端到端测试 (ZAN06UPD - 6 步管线: 编译→生成数据→运行→提取→Java→比对)
python test-data\s30_db_e2e.py
```
期望输出末尾:
```
COVERAGE: DB pipeline E2E PASSED
```
### 5.6 DB 单程序运行
```bash
# 自动路由: 源码含 EXEC SQL → DB 管道,否则走非 DB 管道
python -m cobol_testgen ..\cobol-tna-system\src\ZAN06UPD.cbl
# 带 gcov 覆盖率收集 (编译带 -g --coverage,生成 HTML 报告)
python -m cobol_testgen --gcov ..\cobol-tna-system\src\KIN08DBU.cbl runtime\
```
DB 管道会自动加载 `config/programs/<PROGRAM_ID>.yaml` 中的表定义、子程序列表、运行场景(normal/collision/abnormal),多场景运行的 gcov 覆盖率会被合并输出。
---
## 6. 关键 API
### 6.1 extract_structure(cobol_source)
**输入**: COBOL 程序源码文本
**返回**: dict — 包含总分支数、决策点列表、分支树对象等
```python
st = extract_structure(src)
branches = st["total_branches"] # 总分支数
dps = st["decision_points"] # 决策点列表
tree = st["branch_tree_obj"] # 分支树对象
```
### 6.2 generate_data(cobol_source, structure, copybook_dirs=None)
**输入**:
- `cobol_source`: COBOL 程序原始源码(未预处理)
- `structure`: extract_structure 返回的 dict
- `copybook_dirs`: COPYBOOK 搜索路径列表(可选)
**返回**: list[dict] — 每条记录包含所有字段的值
```python
recs = generate_data(src, st)
# 或带 COPYBOOK 目录
recs = generate_data(src, st, copybook_dirs=["./cpy", "../common/copybooks"])
```
### 6.3 覆盖率数据
`generate_data` 执行后,`structure` 对象包含 `coverage` 键:
```python
cov = st["coverage"]
total = cov["total"] # 总分支数
covered = cov["covered"] # 覆盖分支数
pct = cov["pct"] # 覆盖率百分比
dps = cov["decision_points"] # 各决策点明细
```
---
## 7. DB 管道概述
系统包含两条平行管道,根据 COBOL 源码是否包含 `EXEC SQL` 自动路由:
| 方面 | 非 DB 管道 | DB 管道 |
|------|------------|---------|
| **入口** | `main.py``orchestrator.py` | Python CLI 自动检测 `EXEC SQL``orchestrator_db.py: GixsqlOrchestrator` |
| **编译器** | `cobc -E` + 标准编译 | `gixpp.exe` (ESQL 预处理) + `cobc -l gixsql` |
| **运行时** | 平面二进制文件 (I-O) | SQLite DB (EXEC SQL) + 平面文件 (顺序 I-O) |
| **数据生成** | 分支覆盖记录集 | 分支覆盖记录集 + SQLite 表行 (按 WHERE 约束生成) |
| **程序配置** | 无 | `config/programs/<ID>.yaml` (表定义、子程序、场景) |
| **验证** | 二进制平面文件比对 | SQLite 表行比对 + Java 中间 JSON (W01.json) |
| **覆盖率** | 静态分支覆盖 | 动态 gcov 覆盖率 (跨场景合并) |
| **Java 集成** | Native Java / Spark Java | Java 读取中间 JSON |
### 7.1 DB 管道 6 步流程
| 步骤 | 方法 | 说明 |
|------|------|------|
| **1** | `step1_setup_environment` | gixpp ESQL 预处理 → cobc 编译(链接 libgixsql),避免中文路径问题 |
| **2** | `step2_generate_inputs` | 解析 COBOL → 分支覆盖数据 + SQLite 表行(含正/异常场景) |
| **3** | `step3_run_cobol` | 运行编译后 .exeGIXSQL_DB_PATH 环境变量),收集 gcov |
| **4** | `step4_extract_intermediate` | 读取 SQLite 表 → 中间 JSONW01.json,供 Java 消费) |
| **5** | `step5_run_java` | 执行 Java JAR,以中间 JSON 为输入 |
| **6** | `step6_verify` | Java 输出 vs COBOL 预期值比对 |
### 7.2 前置条件
- **`cobol-tna-system` 同级项目**: DB 程序源码需位于 `../cobol-tna-system/src/`(或通过 `config/programs/*.yaml` 配置路径)
- **`config/programs/<PROGRAM_ID>.yaml`**: 每个 DB 程序必须有对应的 YAML schema,定义:
- `db_tables`: 表名、列类型、主键
- `subprograms`: 子程序列表(编译时链接)
- `runs`: 运行场景(normal / collision / abnormal),每种场景可配置不同的 sysin 和预期输出
- **`C:\Temp\gix\`**: SQLite 数据库存放目录(由 `GIXSQL_DB_PATH` 指定)
- **`COB_LIBRARY_PATH`**: 必须包含 `gixsql/lib/`(运行时载入 `libgixsql.dll`
---
## 8. 运行条件明细(同事配置检查清单)
### 必须满足
- [ ] Python 3.12+ 已安装,在 PATH 中
- [ ] `pip install lark` 执行成功
- [ ] GnuCOBOL (cobc) 3.2.0 已安装,在 PATH 中
- [ ] `cobc --version` 输出正常
- [ ] 无防火墙阻止 `gittea.dev` 的 git 访问
- [ ] `D:\` 盘有至少 500MB 空闲
### 如果使用 GnuCOBOL 编译运行
- [ ] `cobc` 命令可用(`which cobc``where cobc`
- [ ] 子程序 DLL 路径在 `COB_LIBRARY_PATH` 环境中
- [ ] EXEC SQL 需要 SQLite3 支持(GC32-BDB-SP1 版本含)
### DB 管道额外检查
- [ ] `gixsql/bin/gixpp.exe` 存在(vendored ESQL 预处理器)
- [ ] `COB_LIBRARY_PATH` 包含 `<proj>/gixsql/lib/`(运行时 DLL
- [ ] `GIXSQL_DB_PATH` 已设置(如 `C:\Temp\gix`),目录存在
- [ ] `config/programs/<ID>.yaml` 存在(per-program schema
- [ ] `../cobol-tna-system/` 同级目录存在(DB 程序源码)
- [ ] `pip install pyyaml` 执行成功
### 常见问题
| 问题 | 原因 | 解决 |
|------|------|------|
| `ModuleNotFoundError: No module named 'lark'` | 缺少 Lark | `pip install lark` |
| `cobc: command not found` | GnuCOBOL 不在 PATH | 添加 `bin\` 到 PATH |
| `Errno 13 Permission denied` | 文件权限 | 以管理员运行或修改文件权限 |
| `gbk codec can't decode byte` | 编码问题 | 设置 `PYTHONIOENCODING=utf-8` |
| `name 'pp_str' is not defined` | 报告脚本 Bug | 已修复,git pull 最新代码 |
| `EXEC SQL ... not supported` | 需要 DB2/SQLite | 用 GC32-BDB-SP1 版本 GnuCOBOL |
| `gixpp: command not found` | gixsql/bin 不在 PATH | `$env:PATH += ";<proj>\\gixsql\\bin"` |
| `Cannot load libgixsql.dll` | COB_LIBRARY_PATH 未设置 | `$env:COB_LIBRARY_PATH = "<proj>\\gixsql\\lib"` |
| `GIXSQL_DB_PATH not set` | SQLite 路径未指定 | `mkdir C:\Temp\gix -Force; $env:GIXSQL_DB_PATH = "C:\Temp\gix"` |
| `config/programs/<ID>.yaml not found` | DB 程序缺少 schema | 补充对应 YAML 配置文件 |
---
## 9. 测试基准程序说明
系统包含两套测试基准程序:
### 电信计费系统 (37 程序)
```
路径: D:\cobol-java\cobol-test-programs/
COPYBOOK: common/copybooks/
类型: Matching / KeyBreak / Division / CSV / Sort 等
```
### 勤怠管理系统 (22 程序)
```
路径: D:\cobol-java\cobol-tna-system/
COPYBOOK: cpy/
子程序: sub/*.cbl → bin/*.dll
类型: 日企勤怠管理 (打工统计)
DB 程序 (EXEC SQL): KIN02UPD, KIN03EXP, KIN06CLD, KIN08DBU, KIN09CSV, ZAN06UPD — 共 6 个
非 DB 程序: KIN01INP, KIN04CHK, KIN05MAT, KIN07DAI, ZAN01CHKZAN05CAL, KYU01CVTKYU09MRG — 共 16 个
```
---
## 10. 快速启动脚本
### Windows (batch)
```batch
@echo off
cd /d D:\cobol-java\cobol-java-v3
echo === COBOL Test Data Generator ===
echo [1/3] Checking dependencies...
python -c "import lark" 2>nul || pip install lark
echo [2/3] Running regression test...
python test-data\s15_coverage_verification.py
if %errorlevel% neq 0 echo FAILED && exit /b 1
echo [3/3] Running full coverage report...
set PYTHONIOENCODING=utf-8
python test-data\s25_per_program_report.py
echo === DONE ===
```
### Linux/macOS
```bash
#!/bin/bash
cd /path/to/cobol-java-v3
echo "=== COBOL Test Data Generator ==="
echo "[1/3] Checking dependencies..."
python3 -c "import lark" 2>/dev/null || pip3 install lark
echo "[2/3] Running regression test..."
python3 test-data/s15_coverage_verification.py
if [ $? -ne 0 ]; then echo "FAILED"; exit 1; fi
echo "[3/3] Running full coverage report..."
PYTHONIOENCODING=utf-8 python3 test-data/s25_per_program_report.py
echo "=== DONE ==="
```
---
## 11. 版本信息
| 版本 | 日期 | 说明 |
|:----:|:----:|------|
| v3.0 | 2026-06-25 | 当前版本。43/43 程序 100% 分支覆盖 |
| v2.0 | 2026-06-20 | 新 PROCEDURE DIVISION 解析器 + 线性路径枚举 |
| v1.0 | 2026-06-14 | 初始版本,BrParser regex 解析器 |
---
## 附录:覆盖率数据验证方法
系统使用三层验证确保覆盖率数据真实:
1. **S15 测试**: 8 个手动构建的 COBOL 片段,每个决策点的手工分支数与系统检测数逐一对比
2. **所有约束通过 _match_constraint 精确匹配**:约束侧和解析侧的字段名都会去掉下标后再比较
3. **无条件 fallback 已全部移除**:没有 "任何路径到达就标记全部" 的逻辑
```python
# coverage.py 中 _mark_if 的真实覆盖逻辑(无 fallback):
def _mark_if(dp, cons):
# 只有约束侧字段名 == 解析侧字段名时标记覆盖
# 加了防御性下标剥离
if _match_constraint(c, simple):
dp.active_branches.add('T' if c[3] else 'F')
elif _match_constraint(c, inv_simple):
dp.active_branches.add('F')
# 没有任何 else + unconditional add
```