
# 测试

Greph 通过三层测试进行验证：

1. **单元测试**：针对独立组件（解析器、遍历器、编码器、选项）的测试。
2. **预期结果回归语料**：将 Greph 的输出与标准 `grep`、`ripgrep` 和 `ast-grep` 二进制文件在固定场景集上的输出进行比较。
3. **探测驱动的功能矩阵**：验证 `rg` 和 `sg` 包装器的已验证功能范围。

每次推送到 `main` 以及每个拉取请求（`.github/workflows/ci.yml`）都会触发 CI。CI 会运行代码规范检查（`composer cs`）、静态分析（`composer analyse`）、PHPUnit 单元和集成测试套件（`composer test`），并在 PHP 8.4 上运行 `composer audit`。预期结果语料（`composer test:oracle`）不属于 CI 流程；功能矩阵需要通过 `php bin/feature-matrix` 手动重新生成。PHPUnit 测试套件包含一项测试，会在上游 `rg` 和 `sg` 标记为不可用时生成矩阵并检查报告结构，因此单项探测回归不会导致测试失败。

## 从克隆目录开始

本页所有内容都需要在仓库克隆目录中操作。由于 0.2.3，Composer 包仅包含 `src/` 和四个运行时可执行文件；`tests/`、`scenarios/`、`benchmarks/` 以及 `bin/` 中的开发脚本均未包含在其中。

```bash
git clone https://github.com/inline0/greph
cd greph
composer install
```

## 运行测试

```bash
# PHPUnit unit and integration suites
composer test

# Oracle regression corpus
composer test:oracle

# Static analysis
composer analyse

# Coding standards
composer cs
composer cs:fix

# Full release-grade verification (analyse + cs + test + test:oracle)
composer verify
```

`composer verify` 是每次发布的门槛。若未通过，变更就还没有完成。

## 预期结果模型

Greph 遵循由预期结果驱动的验证模型。每个场景都会运行三条命令：

1. **预期结果**：运行 `grep`、`ripgrep` 或 `ast-grep`，并针对场景语料捕获输出。
2. **实际结果**：运行 Greph，并针对相同语料捕获输出。
3. **比较器**：通过结构化差异检查逐行或语义等价性。

如果 Greph 与预期结果不一致，那么 Greph 就是错误的。如果 `grep` 和 `rg` 彼此不一致，场景会在 `oracle_disagreement` 中记录这一分歧，注明 Greph 在 `greph_follows` 中遵循的工具，并将与另一工具的比较设为 `skip`。语料中涵盖了这两种情况。

文本场景会与 `grep` 和 `rg` 比较，AST 和重写场景则与 `sg` 比较。预期结果输出只捕获一次，并提交到每个场景的 `oracle/` 目录中，因此正常运行只需要 PHP；只有在捕获或刷新预期结果输出时才需要外部工具。

实现位于 `tests/Oracle/` 中，场景位于 `scenarios/text/`、`scenarios/ast/`、`scenarios/rewrite/` 和 `scenarios/edge/` 中。

## 场景布局

每个场景都是一个独立的目录：

```
scenarios/text/literal-simple/
├── scenario.json     # name, category, pattern, flags, path
├── setup/            # corpus to search through
├── oracle/           # captured oracle output (grep.txt, rg.txt, rg.json, ...)
├── actual/           # captured greph output (generated, git-ignored)
└── reports/          # comparison results (generated, git-ignored)
```

`scenario.json` 声明模式、标志和搜索路径。可选的 `expectations` 对象可为每个预期结果（`exact`、`semantic` 或 `skip`）设置比较模式；若未设置，文本场景会对 `grep` 和 `rg` 进行精确比较，并对 `rg` JSON 进行语义比较；AST 和重写场景则与 `sg` 比较。

## 运行单个场景

仓库提供了一组小型 CLI，用于处理单个场景。它们是开发工具，而非运行时入口：这些工具与运行时二进制文件一同位于 `bin/` 中，但不属于 Composer 包。每个工具都接受一个相对于 `scenarios/` 的场景名称。

```bash
# Capture oracle output for a scenario (needs grep and rg, or sg)
./bin/oracle text/literal-simple

# Run greph against a scenario, capture its output
./bin/actual text/literal-simple

# Diff oracle vs actual
./bin/compare text/literal-simple

# Full pipeline: oracle (only if missing) -> actual -> compare
./bin/test-scenario text/literal-simple

# Recapture the oracle output first
./bin/test-scenario text/literal-simple --refresh
```

## 运行完整回归测试套件

```bash
# All scenarios (the same as composer test:oracle)
./bin/test-regression

# Filter by category
./bin/test-regression --category text
./bin/test-regression --category ast
./bin/test-regression --category rewrite
./bin/test-regression --category edge

# Print only failures and the summary
./bin/test-regression --fast

# Recapture every oracle before comparing
./bin/test-regression --refresh
```

场景始终一次运行一个。`--jobs N` 会被接受并显示在摘要行中，但不会并行运行任何内容（[#7](https://github.com/inline0/greph/issues/7)）。在全新克隆中，`text/no-ignore` 和 `text/explicit-file-input` 会失败，因为它们位于 `setup/vendor/` 下的测试夹具文件未提交（[#6](https://github.com/inline0/greph/issues/6)）。

回归测试套件是新行为的准入门槛。新增标志、新边缘情况或新错误修复，都需要一个场景来记录针对预期结果的行为。

## 合规性报告

```bash
./bin/verify-compliance
```

`verify-compliance` 本身不会运行任何内容。它会读取上一次场景运行留下的 oracle、实际结果和报告文件，并打印哪些场景通过、哪些失败，以及哪些尚无报告。请先运行 `./bin/test-regression`。`--output report.json` 还会将报告写入 JSON。当你想知道“当前分支是否已准备好发布”时，应该运行此命令。

## 测试驱动开发工作流

添加功能时：

1. 编写一个场景目录，其中包含用于测试新行为的语料库和标志。
2. 运行 `./bin/oracle <scenario>`，捕获规范工具的输出。
3. 在 `src/` 中实现该功能。
4. 运行 `./bin/test-scenario <scenario>`，验证 Greph 是否与 oracle 相符。
5. 运行 `./bin/test-regression --fast`，确保其他内容没有发生回归。
6. 在提交前运行 `composer verify`。

修复错误时：

1. 将导致失败的输入记录为一个场景。
2. 确认 oracle 产生预期输出。
3. 确认 Greph 不会产生预期输出。
4. 修复错误。
5. 确认 Greph 现在与 oracle 相符。
6. 运行 `composer verify`。

该场景会永久保留在语料库中，因此除非 `composer test:oracle`（以及 `composer verify`）失败，否则这个错误不会再次出现。
