Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/c_parser/c_parser_implementation_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ Scope:
inputs.
- [x] Regenerate all fixtures with the standalone golden script.
- [x] Regenerate selected fixtures with the standalone golden script.
- [x] Keep golden regeneration out of the comparison tests.
- [x] Regenerate full C golden suites from comparison tests when
`C_PARSER_UPDATE_GOLDENS=1` is set.
- [x] Create a C error golden generator.
- [x] Store expected error type, message fragments, diagnostic fragments, and
parser entrypoint metadata.
Expand Down
23 changes: 10 additions & 13 deletions docs/c_parser/c_parser_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,10 @@ resolution. The `json` regression inputs
intentionally retain recoverable diagnostics from unsupported constructs; they
do not claim complete library parsing. Remaining parser-suite skips cover the
pinned/provenanced corpus target and compiler-preprocessed `.i`/`#line`
behavior; golden inventory checks also skip deliberately while update mode is
rewriting their baselines. Future implementation branches should activate only
the tests for the capability they implement, then merge those branches back
into `c-parser/main`.
behavior. Golden comparison tests rewrite their baselines when
`C_PARSER_UPDATE_GOLDENS=1` is set. Future implementation branches should
activate only the tests for the capability they implement, then merge those
branches back into `c-parser/main`.

### Declaration Coverage Boundary

Expand Down Expand Up @@ -567,15 +567,12 @@ headers, such as `nanosvg.h` before `nanosvgrast.h`; an input without a
sibling is parsed as a one-file project. Golden filenames use the project
stem, such as `api.json` or `jsmn.json`. Goldens can be regenerated for all
active projects with
`python -m tests.parser.c.generate_c_parser_goldens` or for selected paths
relative to `tests/data/c/` by adding either member filename; any matching
sibling is included automatically. Fatal diagnostic goldens are regenerated
with
`python -m tests.parser.c.errors.generate_c_parser_error_goldens`.
The parser goldens are regenerated by the standalone scripts above, not by
the comparison tests. Until include-expanded parsing is implemented, a paired
project records the source-to-header include edge but parses the `.c` and
`.h` members separately.
`C_PARSER_UPDATE_GOLDENS=1 PYTHONPATH=. pytest -q tests/parser/c/test_c_fixture_suite.py`.
Fatal diagnostic goldens are regenerated with
`C_PARSER_UPDATE_GOLDENS=1 PYTHONPATH=. pytest -q tests/parser/c/test_c_error_fixture_suite.py`.
The standalone generator modules remain available for targeted refreshes.
Until include-expanded parsing is implemented, a paired project records the
source-to-header include edge but parses the `.c` and `.h` members separately.

STB is treated as a family of independent single-file libraries: each
top-level `.h` or `.c` input generates its own one-file project golden rather
Expand Down
10 changes: 5 additions & 5 deletions tests/parser/c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ produces `nanosvg.json`; STB produces one golden per top-level library input.
Regenerate all parser goldens with:

```bash
python -m tests.parser.c.generate_c_parser_goldens
C_PARSER_UPDATE_GOLDENS=1 PYTHONPATH=. pytest -q tests/parser/c/test_c_fixture_suite.py
```

Regenerate selected projects by naming either input relative to
Expand All @@ -65,9 +65,9 @@ Fatal diagnostic fixtures live in `tests/data/c/errors/parser/` and their
expected metadata lives in `fixtures/errors/`. Regenerate them with:

```bash
python -m tests.parser.c.errors.generate_c_parser_error_goldens
C_PARSER_UPDATE_GOLDENS=1 PYTHONPATH=. pytest -q tests/parser/c/test_c_error_fixture_suite.py
```

The comparison tests are read-only. Regenerate the expected files with the
standalone generator, then rerun the tests to compare against the checked-in
baselines.
The standalone generator modules remain available for targeted refreshes, but
the comparison tests also rewrite their checked-in baselines when
`C_PARSER_UPDATE_GOLDENS=1` is set.
30 changes: 30 additions & 0 deletions tests/parser/c/test_c_error_fixture_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""C parser error fixture and diagnostic golden regression tests."""

import json
import os
from pathlib import Path

import pytest
Expand All @@ -21,6 +22,25 @@ def _load_expected_error(expected_path: Path) -> dict:
return json.loads(expected_path.read_text(encoding="utf-8"))


def _dump_expected_error(path: Path, error_type: str, exc, parser: str) -> None:
payload = {
"parser": parser,
"error_type": error_type,
"message_contains": [exc.base_message],
"diagnostic_contains": [
f"error[{exc.code}]",
exc.base_message,
exc.source_line.strip() if exc.source_line else "",
],
}
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")


def _update_mode_enabled() -> bool:
return os.getenv("C_PARSER_UPDATE_GOLDENS", "0") == "1"


def test_c_error_fixture_suite_has_fixtures():
fixtures = [path for path in _ERRORS_DIR.glob("*") if path.suffix.lower() in _SOURCE_SUFFIXES]
assert fixtures, "No C parser error fixtures found in tests/data/c/errors/parser"
Expand All @@ -47,6 +67,16 @@ def test_c_error_fixture_suite_reports_expected_diagnostics():
expected_path = _expected_path_for_fixture(fixture)
source = fixture.read_text(encoding="utf-8")

if _update_mode_enabled():
try:
parse_c_file(source, filename=fixture.name)
raise AssertionError(
f"Expected CParseError from {fixture.name} but no error was raised"
)
except CParseError as exc:
_dump_expected_error(expected_path, "CParseError", exc, "parse_c_file")
continue

expected = _load_expected_error(expected_path)
assert expected["parser"] == "parse_c_file"
assert expected["error_type"] == "CParseError"
Expand Down
20 changes: 19 additions & 1 deletion tests/parser/c/test_c_fixture_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""C parser grouped-project fixture/golden regression tests."""

import json
import os
from pathlib import Path

import pytest
Expand Down Expand Up @@ -64,6 +65,15 @@ def _expected_path_for_project(data_subdir: str, project_key: Path) -> Path:
return (_FIXTURES_DIR / data_subdir / project_key).with_suffix(".json")


def _load_expected(expected_path: Path) -> dict:
return json.loads(expected_path.read_text(encoding="utf-8"))


def _dump_expected(path: Path, parsed: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(parsed, indent=2) + "\n", encoding="utf-8")


def _normalize_resolved_paths(value):
if isinstance(value, dict):
resolved_path = value.get("resolved_path")
Expand Down Expand Up @@ -101,6 +111,10 @@ def _serialize_project(fixtures: list[Path]) -> dict:
return _normalize_resolved_paths(_parse_project(fixtures).to_dict())


def _update_mode_enabled() -> bool:
return os.getenv("C_PARSER_UPDATE_GOLDENS", "0") == "1"


@pytest.mark.parametrize("data_subdir", _FIXTURE_GROUPS)
def test_c_fixture_golden_suite_has_inputs(data_subdir):
fixtures = sorted((_DATA_DIR / data_subdir).glob("*"))
Expand All @@ -125,7 +139,11 @@ def test_c_fixture_golden_suite_compares_project_json(data_subdir):
expected_path = _expected_path_for_project(data_subdir, project_key)
parsed = _serialize_project(fixtures)

expected = json.loads(expected_path.read_text(encoding="utf-8"))
if _update_mode_enabled():
_dump_expected(expected_path, parsed)
continue

expected = _load_expected(expected_path)
assert parsed == expected


Expand Down
Loading