From e58cddd0d4fa3f3f2d20664a530887a4c731b65e Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Sun, 28 Jun 2026 12:56:21 +0200 Subject: [PATCH 1/4] test(extraction): document declarative fixture+snapshot format Add tests/data/extraction/ with a README describing the declarative extraction-test format: per-case YAML fixtures (lang/config/source) plus a normalized JSON snapshot of the extraction output (needs/need_refs/marked_rst/ warnings). Harness + fixtures + snapshots follow in subsequent commits. --- tests/data/extraction/README.md | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/data/extraction/README.md diff --git a/tests/data/extraction/README.md b/tests/data/extraction/README.md new file mode 100644 index 0000000..5123937 --- /dev/null +++ b/tests/data/extraction/README.md @@ -0,0 +1,71 @@ +# Declarative extraction-test fixtures + +Marker extraction (comment → one-line need / need-id-reference / marked-rst) is +tested declaratively: each case is a **fixture** (the input) plus a **snapshot** +(the captured expected output). This keeps the inputs language-agnostic and the +expected output reviewable, and lets us cover the whole language matrix without a +bespoke test function per case. + +## Fixture format + +Each `*.yaml` file in this directory is a map of `case_name → case`: + +```yaml +default_oneliner_cpp: + lang: cpp # cpp | c | python | csharp | rust | yaml | go | jsonc + config: default # "default", or an inline config block (see below) + source: | + // @My Title, IMPL_1, impl, [REQ_1] + void f() {} + +custom_brackets_c: + lang: c + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: title, type: str} + - {name: id, type: str} + - {name: type, type: str, default: impl} + - {name: links, type: "list[str]", default: []} + need_id_markers: ["@need-ids:"] # optional; default ["@need-ids:"] + source: | + /* [[A Title, ID_1, impl, [REQ_1]]] */ +``` + +- `config: default` uses the built-in `OneLineCommentStyle` default + (`@` / newline / `,` with fields `title, id, type(default "impl"), links(list)`). +- Field `type` is `str` or `list[str]`. + +## Snapshot (expected output) — normalized contract + +Each case is run through the extractor and the result is normalized to this JSON +shape, then compared to a committed snapshot under +`tests/__snapshots__/extraction/`: + +```json +{ + "needs": [{"id": "", "title": "", "type": "", "links": {"field": ["..."]}, "line": 1}], + "need_refs": [{"need_id": "", "line": 1}], + "marked_rst": [{"content": "", "start_line": 1, "end_line": 1}], + "warnings": [{"kind": "too_many_fields", "line": 1}] +} +``` + +Lines are 1-indexed. `needs`/`warnings` are sorted by line; `need_refs` by +`(line, need_id)`. Volatile data (file paths, columns, URLs, tagged scope) is +omitted so snapshots are stable. + +## Running / updating + +```bash +# run the declarative extraction tests +python -m pytest tests/test_extraction_fixtures.py + +# review + accept snapshot changes after editing fixtures or the extractor +python -m pytest tests/test_extraction_fixtures.py --snapshot-update +``` + +Adding a case is just a new entry in a `*.yaml` file here, then +`--snapshot-update` to capture its snapshot (review the diff before committing). From b877259f8799aecf821e29986349915fe3f7b152 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Sun, 28 Jun 2026 13:01:19 +0200 Subject: [PATCH 2/4] test(extraction): declarative fixture+snapshot harness + language matrix Add a parametrized harness that loads tests/data/extraction/*.yaml cases (lang/config/source), runs SourceAnalyse on a temp source file, normalizes the output to {needs,need_refs,marked_rst,warnings} (1-indexed lines) and asserts a single-file JSON snapshot per case (ExtractionSnapshotExtension). Fixtures: default one-liners across all eight languages (cpp,c,python,csharp, rust,yaml in oneline.yaml; go,jsonc in extra_languages.yaml). --- ...[extra_languages-default_oneliner_go].json | 19 +++ ...tra_languages-default_oneliner_jsonc].json | 19 +++ ...n_fixture[oneline-default_oneliner_c].json | 19 +++ ...fixture[oneline-default_oneliner_cpp].json | 19 +++ ...ture[oneline-default_oneliner_csharp].json | 19 +++ ...ture[oneline-default_oneliner_python].json | 19 +++ ...ixture[oneline-default_oneliner_rust].json | 19 +++ ...ixture[oneline-default_oneliner_yaml].json | 19 +++ tests/conftest.py | 18 +++ tests/data/extraction/extra_languages.yaml | 13 ++ tests/data/extraction/oneline.yaml | 42 +++++ tests/test_extraction_fixtures.py | 144 ++++++++++++++++++ 12 files changed, 369 insertions(+) create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_go].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_jsonc].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_c].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_cpp].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_csharp].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_python].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_rust].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_yaml].json create mode 100644 tests/data/extraction/extra_languages.yaml create mode 100644 tests/data/extraction/oneline.yaml create mode 100644 tests/test_extraction_fixtures.py diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_go].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_go].json new file mode 100644 index 0000000..bdef65e --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_go].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_GO", + "title": "Go Title", + "type": "impl", + "links": { + "links": [ + "REQ_GO" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_jsonc].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_jsonc].json new file mode 100644 index 0000000..ea4fa5a --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[extra_languages-default_oneliner_jsonc].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_JSONC", + "title": "Jsonc Title", + "type": "impl", + "links": { + "links": [ + "REQ_JSONC" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_c].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_c].json new file mode 100644 index 0000000..077b1b4 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_c].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_C", + "title": "C Title", + "type": "impl", + "links": { + "links": [ + "REQ_C" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_cpp].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_cpp].json new file mode 100644 index 0000000..3b92550 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_cpp].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_1", + "title": "My Title", + "type": "impl", + "links": { + "links": [ + "REQ_1" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_csharp].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_csharp].json new file mode 100644 index 0000000..9675823 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_csharp].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_CS", + "title": "Cs Title", + "type": "impl", + "links": { + "links": [ + "REQ_CS" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_python].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_python].json new file mode 100644 index 0000000..58e496a --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_python].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_PY", + "title": "Py Title", + "type": "impl", + "links": { + "links": [ + "REQ_PY" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_rust].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_rust].json new file mode 100644 index 0000000..90e9a28 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_rust].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_RS", + "title": "Rs Title", + "type": "impl", + "links": { + "links": [ + "REQ_RS" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_yaml].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_yaml].json new file mode 100644 index 0000000..38f198e --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[oneline-default_oneliner_yaml].json @@ -0,0 +1,19 @@ +{ + "needs": [ + { + "id": "IMPL_YAML", + "title": "Yaml Title", + "type": "impl", + "links": { + "links": [ + "REQ_YAML" + ] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 47d6f78..fd9f431 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -101,3 +101,21 @@ def snapshot_marks(snapshot): Sanitize the reqif, to make the snapshots reproducible. """ return snapshot.with_defaults(extension_class=AnchorsSnapshotExtension) + + +class ExtractionSnapshotExtension(SingleFileSnapshotExtension): + """Single-file JSON snapshots for the declarative extraction tests.""" + + _write_mode = WriteMode.TEXT + file_extension = "json" + + def serialize(self, data, **_kwargs): + if not isinstance(data, dict): + raise TypeError(f"Expected dict, got {type(data)}") + return json.dumps(data, indent=2) + + +@pytest.fixture +def snapshot_extraction(snapshot): + """Snapshot fixture for the normalized extraction output (one JSON per case).""" + return snapshot.with_defaults(extension_class=ExtractionSnapshotExtension) diff --git a/tests/data/extraction/extra_languages.yaml b/tests/data/extraction/extra_languages.yaml new file mode 100644 index 0000000..cda1a57 --- /dev/null +++ b/tests/data/extraction/extra_languages.yaml @@ -0,0 +1,13 @@ +default_oneliner_go: + lang: go + config: default + source: | + // @Go Title, IMPL_GO, impl, [REQ_GO] + func f() {} + +default_oneliner_jsonc: + lang: jsonc + config: default + source: | + // @Jsonc Title, IMPL_JSONC, impl, [REQ_JSONC] + { "k": 1 } diff --git a/tests/data/extraction/oneline.yaml b/tests/data/extraction/oneline.yaml new file mode 100644 index 0000000..afd9c70 --- /dev/null +++ b/tests/data/extraction/oneline.yaml @@ -0,0 +1,42 @@ +default_oneliner_cpp: + lang: cpp + config: default + source: | + // @My Title, IMPL_1, impl, [REQ_1] + void f() {} + +default_oneliner_c: + lang: c + config: default + source: | + // @C Title, IMPL_C, impl, [REQ_C] + void g(void) {} + +default_oneliner_python: + lang: python + config: default + source: | + # @Py Title, IMPL_PY, impl, [REQ_PY] + def f(): + pass + +default_oneliner_csharp: + lang: csharp + config: default + source: | + // @Cs Title, IMPL_CS, impl, [REQ_CS] + class C {} + +default_oneliner_rust: + lang: rust + config: default + source: | + // @Rs Title, IMPL_RS, impl, [REQ_RS] + fn f() {} + +default_oneliner_yaml: + lang: yaml + config: default + source: | + # @Yaml Title, IMPL_YAML, impl, [REQ_YAML] + key: value diff --git a/tests/test_extraction_fixtures.py b/tests/test_extraction_fixtures.py new file mode 100644 index 0000000..fff52d6 --- /dev/null +++ b/tests/test_extraction_fixtures.py @@ -0,0 +1,144 @@ +"""Declarative marker-extraction tests. + +Each case in ``tests/data/extraction/*.yaml`` supplies an input (``lang`` + +``config`` + ``source``); the extractor is run on it and the normalized output is +compared to a committed JSON snapshot. See ``tests/data/extraction/README.md``. +""" + +import tempfile +from pathlib import Path + +import pytest +import yaml + +from sphinx_codelinks.analyse.analyse import SourceAnalyse +from sphinx_codelinks.config import ( + NeedIdRefsConfig, + OneLineCommentStyle, + SourceAnalyseConfig, +) +from sphinx_codelinks.source_discover.config import CommentType + +FIXTURE_DIR = Path(__file__).parent / "data" / "extraction" + +# fixture ``lang`` -> (comment type, source-file extension) +LANG_MAP: dict[str, tuple[CommentType, str]] = { + "cpp": (CommentType.cpp, "cpp"), + "c": (CommentType.cpp, "c"), + "python": (CommentType.python, "py"), + "csharp": (CommentType.cs, "cs"), + "rust": (CommentType.rust, "rs"), + "yaml": (CommentType.yaml, "yaml"), + "go": (CommentType.go, "go"), + "jsonc": (CommentType.jsonc, "jsonc"), +} + + +def _load_cases() -> list: + cases = [] + for path in sorted(FIXTURE_DIR.glob("*.yaml")): + data = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + for name, case in data.items(): + cases.append(pytest.param(case, id=f"{path.stem}-{name}")) + return cases + + +def _build_oneline_style(config) -> OneLineCommentStyle: + if config in (None, "default"): + return OneLineCommentStyle() + kwargs = { + key: config[key] + for key in ("start_sequence", "end_sequence", "field_split_char") + if key in config + } + if "needs_fields" in config: + kwargs["needs_fields"] = config["needs_fields"] + return OneLineCommentStyle(**kwargs) + + +def _list_field_names(style: OneLineCommentStyle) -> set[str]: + return {f["name"] for f in style.needs_fields if f.get("type") == "list[str]"} + + +def _normalize(analyse: SourceAnalyse, style: OneLineCommentStyle) -> dict: + core = {"id", "title", "type"} + list_fields = _list_field_names(style) + + needs = [] + for n in analyse.oneline_needs: + need = n.need + links = {name: need[name] for name in list_fields if name in need} + metadata = { + k: v for k, v in need.items() if k not in core and k not in list_fields + } + needs.append( + { + "id": need.get("id", ""), + "title": need.get("title", ""), + "type": need.get("type", ""), + "links": links, + "metadata": metadata, + "line": n.source_map["start"]["row"] + 1, + } + ) + needs.sort(key=lambda d: (d["line"], d["id"])) + + need_refs = [] + for ref in analyse.need_id_refs: + line = ref.source_map["start"]["row"] + 1 + for need_id in ref.need_ids: + need_refs.append({"need_id": need_id, "line": line}) + need_refs.sort(key=lambda d: (d["line"], d["need_id"])) + + marked_rst = [ + { + "content": m.rst, + "start_line": m.source_map["start"]["row"] + 1, + "end_line": m.source_map["end"]["row"] + 1, + } + for m in analyse.marked_rst + ] + marked_rst.sort(key=lambda d: d["start_line"]) + + warnings = [ + {"kind": w.sub_type, "line": w.lineno} for w in analyse.oneline_warnings + ] + warnings.sort(key=lambda d: (d["line"], d["kind"])) + + return { + "needs": needs, + "need_refs": need_refs, + "marked_rst": marked_rst, + "warnings": warnings, + } + + +@pytest.mark.parametrize("case", _load_cases()) +def test_extraction_fixture(case: dict, tmp_path: Path, snapshot_extraction) -> None: + comment_type, ext = LANG_MAP[case["lang"]] + style = _build_oneline_style(case.get("config")) + + markers = case.get("config", {}).get("need_id_markers") if isinstance( + case.get("config"), dict + ) else None + refs_config = NeedIdRefsConfig(markers=markers) if markers else NeedIdRefsConfig() + + src_path = tmp_path / f"case.{ext}" + src_path.write_text(case["source"], encoding="utf-8") + + cfg = SourceAnalyseConfig( + src_files=[src_path], + src_dir=tmp_path, + comment_type=comment_type, + get_oneline_needs=True, + get_need_id_refs=True, + get_rst=True, + oneline_comment_style=style, + need_id_refs_config=refs_config, + ) + analyse = SourceAnalyse(cfg) + analyse.git_remote_url = None + analyse.git_commit_rev = None + analyse.run() + + assert snapshot_extraction == _normalize(analyse, style) From 7d6d516a844e354b7dc92566bfe27baf9522d34a Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Sun, 28 Jun 2026 13:02:28 +0200 Subject: [PATCH 3/4] test(extraction): satisfy ruff (imports, extend, ternary) --- tests/test_extraction_fixtures.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/test_extraction_fixtures.py b/tests/test_extraction_fixtures.py index fff52d6..13f8c68 100644 --- a/tests/test_extraction_fixtures.py +++ b/tests/test_extraction_fixtures.py @@ -5,7 +5,6 @@ compared to a committed JSON snapshot. See ``tests/data/extraction/README.md``. """ -import tempfile from pathlib import Path import pytest @@ -86,8 +85,7 @@ def _normalize(analyse: SourceAnalyse, style: OneLineCommentStyle) -> dict: need_refs = [] for ref in analyse.need_id_refs: line = ref.source_map["start"]["row"] + 1 - for need_id in ref.need_ids: - need_refs.append({"need_id": need_id, "line": line}) + need_refs.extend({"need_id": need_id, "line": line} for need_id in ref.need_ids) need_refs.sort(key=lambda d: (d["line"], d["need_id"])) marked_rst = [ @@ -116,11 +114,10 @@ def _normalize(analyse: SourceAnalyse, style: OneLineCommentStyle) -> dict: @pytest.mark.parametrize("case", _load_cases()) def test_extraction_fixture(case: dict, tmp_path: Path, snapshot_extraction) -> None: comment_type, ext = LANG_MAP[case["lang"]] - style = _build_oneline_style(case.get("config")) + config = case.get("config") + style = _build_oneline_style(config) - markers = case.get("config", {}).get("need_id_markers") if isinstance( - case.get("config"), dict - ) else None + markers = config.get("need_id_markers") if isinstance(config, dict) else None refs_config = NeedIdRefsConfig(markers=markers) if markers else NeedIdRefsConfig() src_path = tmp_path / f"case.{ext}" From 83d6cd1fd55180fcec63317a946a6106f211f1b8 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Sun, 28 Jun 2026 13:14:01 +0200 Subject: [PATCH 4/4] test(extraction): escapes, custom configs, need-refs, warnings, rst fixtures Add declarative fixtures + JSON snapshots for: escape-sequence resolution (escapes.yaml), custom one-line configs incl bracketed + space-separated (custom_config.yaml), need-id references with default/custom markers (need_refs.yaml), four parser warning kinds (warnings.yaml), and @rst/@endrst blocks (marked_rst.yaml). Add an 'extract' fixture toggle (subset of oneline/need_refs/rst, default all) so a focused case isn't polluted by the '@' one-line start also matching '@need-ids:'/'@rst'. 25 cases total; all snapshots captured. --- ...om_config-bracketed_defaults_applied].json | 20 +++++ ...re[custom_config-bracketed_two_links].json | 23 ++++++ ...ixture[custom_config-space_separated].json | 15 ++++ ...fig-space_separated_defaults_applied].json | 15 ++++ ...capes-escaped_backslash_in_str_field].json | 22 ++++++ ...scapes-escaped_brackets_in_list_item].json | 22 ++++++ ...e[escapes-escaped_comma_in_list_item].json | 22 ++++++ ...s-escaped_comma_in_str_field_default].json | 17 +++++ ...re[marked_rst-multi_line_rst_doxygen].json | 12 +++ ...n_fixture[marked_rst-single_line_rst].json | 12 +++ ...tion_fixture[need_refs-custom_marker].json | 15 ++++ ...ion_fixture[need_refs-default_marker].json | 19 +++++ ...ture[need_refs-default_marker_python].json | 11 +++ ...ure[warnings-missing_square_brackets].json | 11 +++ ...ot_start_or_end_with_square_brackets].json | 11 +++ ...tion_fixture[warnings-too_few_fields].json | 11 +++ ...ion_fixture[warnings-too_many_fields].json | 11 +++ tests/data/extraction/README.md | 4 + tests/data/extraction/custom_config.yaml | 74 +++++++++++++++++++ tests/data/extraction/escapes.yaml | 70 ++++++++++++++++++ tests/data/extraction/marked_rst.yaml | 39 ++++++++++ tests/data/extraction/need_refs.yaml | 36 +++++++++ tests/data/extraction/warnings.yaml | 53 +++++++++++++ tests/test_extraction_fixtures.py | 11 ++- 24 files changed, 553 insertions(+), 3 deletions(-) create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_defaults_applied].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_two_links].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated_defaults_applied].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_backslash_in_str_field].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_brackets_in_list_item].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_list_item].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_str_field_default].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-custom_marker].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker_python].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-missing_square_brackets].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-not_start_or_end_with_square_brackets].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_few_fields].json create mode 100644 tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_many_fields].json create mode 100644 tests/data/extraction/custom_config.yaml create mode 100644 tests/data/extraction/escapes.yaml create mode 100644 tests/data/extraction/marked_rst.yaml create mode 100644 tests/data/extraction/need_refs.yaml create mode 100644 tests/data/extraction/warnings.yaml diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_defaults_applied].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_defaults_applied].json new file mode 100644 index 0000000..bb2a53d --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_defaults_applied].json @@ -0,0 +1,20 @@ +{ + "needs": [ + { + "id": "IMPL_1", + "title": "title 1", + "type": "impl", + "links": { + "links": [] + }, + "metadata": { + "status": "open", + "priority": "low" + }, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_two_links].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_two_links].json new file mode 100644 index 0000000..831235c --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-bracketed_two_links].json @@ -0,0 +1,23 @@ +{ + "needs": [ + { + "id": "IMPL_5", + "title": "title 5", + "type": "impl", + "links": { + "links": [ + "SPEC_1", + "SPEC_2" + ] + }, + "metadata": { + "status": "open", + "priority": "low" + }, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated].json new file mode 100644 index 0000000..4f4305c --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated].json @@ -0,0 +1,15 @@ +{ + "needs": [ + { + "id": "FEAT_001", + "title": "MyFeature", + "type": "feature", + "links": {}, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated_defaults_applied].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated_defaults_applied].json new file mode 100644 index 0000000..8c3b796 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[custom_config-space_separated_defaults_applied].json @@ -0,0 +1,15 @@ +{ + "needs": [ + { + "id": "IMPL_1", + "title": "Implementation", + "type": "impl", + "links": {}, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_backslash_in_str_field].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_backslash_in_str_field].json new file mode 100644 index 0000000..0c9d64e --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_backslash_in_str_field].json @@ -0,0 +1,22 @@ +{ + "needs": [ + { + "id": "IMPL_13", + "title": "title\\ 13", + "type": "impl", + "links": { + "links": [ + "[SPEC,_1]" + ] + }, + "metadata": { + "status": "open", + "priority": "low" + }, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_brackets_in_list_item].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_brackets_in_list_item].json new file mode 100644 index 0000000..e113391 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_brackets_in_list_item].json @@ -0,0 +1,22 @@ +{ + "needs": [ + { + "id": "IMPL_12", + "title": "title 12", + "type": "impl", + "links": { + "links": [ + "[SPEC,_1]" + ] + }, + "metadata": { + "status": "open", + "priority": "low" + }, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_list_item].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_list_item].json new file mode 100644 index 0000000..a96c731 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_list_item].json @@ -0,0 +1,22 @@ +{ + "needs": [ + { + "id": "IMPL_11", + "title": "title 11", + "type": "impl", + "links": { + "links": [ + "SPEC,_1" + ] + }, + "metadata": { + "status": "open", + "priority": "low" + }, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_str_field_default].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_str_field_default].json new file mode 100644 index 0000000..8b2f941 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[escapes-escaped_comma_in_str_field_default].json @@ -0,0 +1,17 @@ +{ + "needs": [ + { + "id": "IMPL_3", + "title": "title, 3", + "type": "impl", + "links": { + "links": [] + }, + "metadata": {}, + "line": 1 + } + ], + "need_refs": [], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json new file mode 100644 index 0000000..52dc460 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-multi_line_rst_doxygen].json @@ -0,0 +1,12 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [ + { + "content": " .. impl:: Multi\n :id: M_1\n\n Body line.\n ", + "start_line": 3, + "end_line": 3 + } + ], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json new file mode 100644 index 0000000..63dc6db --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[marked_rst-single_line_rst].json @@ -0,0 +1,12 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [ + { + "content": " .. impl:: X\\n :id: Y\\n", + "start_line": 2, + "end_line": 2 + } + ], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-custom_marker].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-custom_marker].json new file mode 100644 index 0000000..10c301e --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-custom_marker].json @@ -0,0 +1,15 @@ +{ + "needs": [], + "need_refs": [ + { + "need_id": "REQ_A", + "line": 1 + }, + { + "need_id": "REQ_B", + "line": 1 + } + ], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker].json new file mode 100644 index 0000000..4068e4f --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker].json @@ -0,0 +1,19 @@ +{ + "needs": [], + "need_refs": [ + { + "need_id": "REQ_1", + "line": 1 + }, + { + "need_id": "REQ_2", + "line": 1 + }, + { + "need_id": "REQ_3", + "line": 1 + } + ], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker_python].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker_python].json new file mode 100644 index 0000000..b015e26 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[need_refs-default_marker_python].json @@ -0,0 +1,11 @@ +{ + "needs": [], + "need_refs": [ + { + "need_id": "REQ_1", + "line": 1 + } + ], + "marked_rst": [], + "warnings": [] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-missing_square_brackets].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-missing_square_brackets].json new file mode 100644 index 0000000..6fba384 --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-missing_square_brackets].json @@ -0,0 +1,11 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [], + "warnings": [ + { + "kind": "missing_square_brackets", + "line": 1 + } + ] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-not_start_or_end_with_square_brackets].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-not_start_or_end_with_square_brackets].json new file mode 100644 index 0000000..75d290f --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-not_start_or_end_with_square_brackets].json @@ -0,0 +1,11 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [], + "warnings": [ + { + "kind": "not_start_or_end_with_square_brackets", + "line": 1 + } + ] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_few_fields].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_few_fields].json new file mode 100644 index 0000000..abaa9cb --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_few_fields].json @@ -0,0 +1,11 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [], + "warnings": [ + { + "kind": "too_few_fields", + "line": 1 + } + ] +} \ No newline at end of file diff --git a/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_many_fields].json b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_many_fields].json new file mode 100644 index 0000000..ec2081d --- /dev/null +++ b/tests/__snapshots__/test_extraction_fixtures/test_extraction_fixture[warnings-too_many_fields].json @@ -0,0 +1,11 @@ +{ + "needs": [], + "need_refs": [], + "marked_rst": [], + "warnings": [ + { + "kind": "too_many_fields", + "line": 1 + } + ] +} \ No newline at end of file diff --git a/tests/data/extraction/README.md b/tests/data/extraction/README.md index 5123937..84f8efe 100644 --- a/tests/data/extraction/README.md +++ b/tests/data/extraction/README.md @@ -37,6 +37,10 @@ custom_brackets_c: - `config: default` uses the built-in `OneLineCommentStyle` default (`@` / newline / `,` with fields `title, id, type(default "impl"), links(list)`). - Field `type` is `str` or `list[str]`. +- `extract` (optional): which extractors to run — a subset of + `[oneline, need_refs, rst]` (default: all three). Narrow it to keep a case + focused: a need-reference case sets `extract: [need_refs]` so the `@`-prefixed + `@need-ids:` marker isn't also parsed as a one-line need. ## Snapshot (expected output) — normalized contract diff --git a/tests/data/extraction/custom_config.yaml b/tests/data/extraction/custom_config.yaml new file mode 100644 index 0000000..a1f316f --- /dev/null +++ b/tests/data/extraction/custom_config.yaml @@ -0,0 +1,74 @@ +# Non-default one-line comment configurations. +# +# (a) A bracketed multi-field config (``[[`` / ``]]``, six fields). The two extra +# str fields (``status``, ``priority``) are not core (id/title/type) and not +# list fields, so the harness surfaces them under ``metadata`` in the snapshot. +# (b) A space-separated config (``@`` start, newline end, `` `` split char) with +# three fields ``title id type``. + +bracketed_defaults_applied: + # Only id + title given; type/links/status/priority fall back to their defaults. + # status=open and priority=low appear under ``metadata``. + lang: cpp + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: id} + - {name: title} + - {name: type, default: impl} + - {name: links, type: "list[str]", default: []} + - {name: status, default: open} + - {name: priority, default: low} + source: | + /* [[IMPL_1, title 1]] */ + void f() {} + +bracketed_two_links: + # All fields given explicitly; links holds two ids and status=open is explicit. + lang: cpp + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: id} + - {name: title} + - {name: type, default: impl} + - {name: links, type: "list[str]", default: []} + - {name: status, default: open} + - {name: priority, default: low} + source: | + /* [[IMPL_5, title 5, impl, [SPEC_1, SPEC_2], open]] */ + void f() {} + +space_separated: + # Space as the field split char: title=MyFeature, id=FEAT_001, type=feature. + lang: cpp + config: + start_sequence: "@" + end_sequence: "\n" + field_split_char: " " + needs_fields: + - {name: title} + - {name: id} + - {name: type, default: impl} + source: | + // @MyFeature FEAT_001 feature + void f() {} + +space_separated_defaults_applied: + # Space-separated config with only title + id given; type defaults to impl. + lang: cpp + config: + start_sequence: "@" + end_sequence: "\n" + field_split_char: " " + needs_fields: + - {name: title} + - {name: id} + - {name: type, default: impl} + source: | + // @Implementation IMPL_1 + void f() {} diff --git a/tests/data/extraction/escapes.yaml b/tests/data/extraction/escapes.yaml new file mode 100644 index 0000000..f8bc2e0 --- /dev/null +++ b/tests/data/extraction/escapes.yaml @@ -0,0 +1,70 @@ +# Escape-sequence handling in one-line fields. +# +# In YAML ``source: |`` block scalars backslashes are literal, so the comment +# text below is byte-for-byte what the extractor sees. ESCAPE is ``\``. +# +# The bracketed multi-field config (``[[`` / ``]]`` with id, title, type, links, +# status, priority) is used for the list-field and str-field backslash cases so +# the escaped chars land in a ``list[str]`` field and a trailing ``str`` field. + +escaped_comma_in_list_item: + # ``[SPEC\,_1]`` -> a single link literally containing a comma: ``SPEC,_1``. + lang: cpp + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: id} + - {name: title} + - {name: type, default: impl} + - {name: links, type: "list[str]", default: []} + - {name: status, default: open} + - {name: priority, default: low} + source: | + /* [[IMPL_11, title 11, impl, [SPEC\,_1], open]] */ + void f() {} + +escaped_brackets_in_list_item: + # ``[\[SPEC\,_1\]]`` -> one link literally containing brackets: ``[SPEC,_1]``. + lang: cpp + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: id} + - {name: title} + - {name: type, default: impl} + - {name: links, type: "list[str]", default: []} + - {name: status, default: open} + - {name: priority, default: low} + source: | + /* [[IMPL_12, title 12, impl, [\[SPEC\,_1\]], open]] */ + void f() {} + +escaped_backslash_in_str_field: + # ``title\\ 13`` (escaped backslash in a str field) -> ``title\ 13``. + lang: cpp + config: + start_sequence: "[[" + end_sequence: "]]" + field_split_char: "," + needs_fields: + - {name: id} + - {name: title} + - {name: type, default: impl} + - {name: links, type: "list[str]", default: []} + - {name: status, default: open} + - {name: priority, default: low} + source: | + /* [[IMPL_13, title\\ 13, impl, [\[SPEC\,_1\]], open]] */ + void f() {} + +escaped_comma_in_str_field_default: + # Default config: an escaped comma in the ``title`` str field -> ``title, 3``. + lang: cpp + config: default + source: | + // @title\, 3, IMPL_3 + void f() {} diff --git a/tests/data/extraction/marked_rst.yaml b/tests/data/extraction/marked_rst.yaml new file mode 100644 index 0000000..409e16a --- /dev/null +++ b/tests/data/extraction/marked_rst.yaml @@ -0,0 +1,39 @@ +# Marked reStructuredText blocks (``@rst`` ... ``@endrst``) inside comments. +# cpp only. Default marked-rst markers (@rst / @endrst) are used. These cases set +# ``extract: [rst]`` so only rst extraction runs (the ``@rst`` / ``@endrst`` lines +# also begin with ``@``, so running the one-line parser here would add noise). +# +# In the snapshot, marked_rst entries carry ``content`` plus ``start_line`` and +# ``end_line`` (both 1-indexed; the extractor maps both to the row of the @rst +# marker, so they coincide). For a single-line block the content is the verbatim +# text between the markers (leading/trailing spaces kept). For a multi-line block +# the rst starts on the line after @rst, ends before @endrst, and the leading +# ``*`` of each doxygen line is stripped from the content. + +single_line_rst: + # @rst and @endrst on one line inside a /* */ block comment. Backslashes in a + # YAML ``|`` scalar are literal, so ``\n`` here is the two-char sequence, not a + # real newline -- the block stays single-line and content is kept verbatim. + lang: cpp + config: default + extract: [rst] + source: | + /* @rst .. impl:: X\n :id: Y\n@endrst */ + void f() {} + +multi_line_rst_doxygen: + # A doxygen /** ... */ block with a leading ``*`` on each line; confirm the + # leading ``*`` is stripped from the extracted content. + lang: cpp + config: default + extract: [rst] + source: | + /** + * @rst + * .. impl:: Multi + * :id: M_1 + * + * Body line. + * @endrst + */ + void f() {} diff --git a/tests/data/extraction/need_refs.yaml b/tests/data/extraction/need_refs.yaml new file mode 100644 index 0000000..c5c6ed1 --- /dev/null +++ b/tests/data/extraction/need_refs.yaml @@ -0,0 +1,36 @@ +# Need-id references (``need_refs`` in the snapshot). +# +# The marker list comes from ``need_id_markers`` in the config block (the harness +# wires it into NeedIdRefsConfig); the default marker is ``@need-ids:``. These +# cases set ``extract: [need_refs]`` so only reference extraction runs (the +# default one-line start is also ``@``, so running the one-line parser here would +# add unrelated noise). + +default_marker: + # Default ``@need-ids:`` marker -> three need_refs (REQ_1, REQ_2, REQ_3). + lang: cpp + config: default + extract: [need_refs] + source: | + // @need-ids: REQ_1, REQ_2, REQ_3 + void f() {} + +custom_marker: + # Custom marker ``REFS:`` via need_id_markers -> two need_refs (REQ_A, REQ_B). + lang: cpp + config: + need_id_markers: ["REFS:"] + extract: [need_refs] + source: | + // REFS: REQ_A, REQ_B + void f() {} + +default_marker_python: + # Same default marker, different source language (Python ``#`` comment). + lang: python + config: default + extract: [need_refs] + source: | + # @need-ids: REQ_1 + def f(): + pass diff --git a/tests/data/extraction/warnings.yaml b/tests/data/extraction/warnings.yaml new file mode 100644 index 0000000..3837cd2 --- /dev/null +++ b/tests/data/extraction/warnings.yaml @@ -0,0 +1,53 @@ +# One-line parser warnings. Each ``kind`` is a snake_case string under +# ``warnings`` in the snapshot. The default config (4 fields: title, id, type, +# links) is used unless a kind needs otherwise. + +too_many_fields: + # 5 fields supplied, config defines 4 -> too_many_fields. + lang: cpp + config: default + source: | + // @title, IMPL_1, impl, [REQ_1], extra + void f() {} + +too_few_fields: + # Only one field supplied; title + type are required (2) -> too_few_fields. + lang: cpp + config: default + source: | + // @only + void f() {} + +missing_square_brackets: + # links is type list[str] but given without ``[ ]`` -> missing_square_brackets. + lang: cpp + config: default + source: | + // @title, IMPL_1, impl, REQ_1 + void f() {} + +not_start_or_end_with_square_brackets: + # links given as ``x[REQ]`` (does not start with ``[``) + # -> not_start_or_end_with_square_brackets. + lang: cpp + config: default + source: | + // @title, IMPL_1, impl, x[REQ] + void f() {} + +# NOTE: newline_in_field is intentionally absent. +# +# This warning fires only when ``oneline_parser`` receives a single string whose +# extracted field span contains an interior newline (``is_newline_in_field``). +# The analyse pipeline, however, runs the parser per *physical line*: +# ``SourceAnalyse.extract_oneline_need`` does ``text.splitlines(keepends=True)`` +# and parses each element separately, so no string handed to the parser ever +# contains an interior newline (only a trailing one, which the end_sequence +# consumes). Block comments spanning lines therefore degrade to other warnings +# (e.g. too_few_fields / missing_square_brackets) instead. +# +# A genuine block-comment attempt -- ``/* @title]] , IMPL */`` -- was +# tried and produced too_few_fields on the first line, not newline_in_field. +# The kind is reachable only via a direct ``oneline_parser`` unit call (see +# tests/test_oneline_parser.py), which this declarative harness does not do, so +# it cannot be triggered here without faking it. Left out deliberately. diff --git a/tests/test_extraction_fixtures.py b/tests/test_extraction_fixtures.py index 13f8c68..caf11be 100644 --- a/tests/test_extraction_fixtures.py +++ b/tests/test_extraction_fixtures.py @@ -120,6 +120,11 @@ def test_extraction_fixture(case: dict, tmp_path: Path, snapshot_extraction) -> markers = config.get("need_id_markers") if isinstance(config, dict) else None refs_config = NeedIdRefsConfig(markers=markers) if markers else NeedIdRefsConfig() + # Which extractors to run. Defaults to all; a fixture narrows this (e.g. a + # need-refs case sets ``extract: [need_refs]``) so unrelated markers — like + # ``@need-ids:`` matching the ``@`` one-line start — don't add noise. + extract = case.get("extract", ["oneline", "need_refs", "rst"]) + src_path = tmp_path / f"case.{ext}" src_path.write_text(case["source"], encoding="utf-8") @@ -127,9 +132,9 @@ def test_extraction_fixture(case: dict, tmp_path: Path, snapshot_extraction) -> src_files=[src_path], src_dir=tmp_path, comment_type=comment_type, - get_oneline_needs=True, - get_need_id_refs=True, - get_rst=True, + get_oneline_needs="oneline" in extract, + get_need_id_refs="need_refs" in extract, + get_rst="rst" in extract, oneline_comment_style=style, need_id_refs_config=refs_config, )