[docs]  1# @Test suite for needextend RST generation from extracted markers, TEST_WRITE_1, test, [IMPL_CLI_WRITE]
  2from pathlib import Path
  3
  4import pytest
  5
  6from sphinx_codelinks.analyse.analyse import SourceAnalyse
  7from sphinx_codelinks.analyse.models import MarkedContentType
  8from sphinx_codelinks.config import SourceAnalyseConfig
  9from sphinx_codelinks.needextend_write import (
 10    MarkedContentSchema,
 11    convert_marked_content,
 12)
 13
 14
 15@pytest.mark.parametrize(
 16    ("markers", "texts"),
 17    [
 18        (
 19            [
 20                {
 21                    "filepath": "/home/jui-wen/git_repo/ub/sphinx-codelinks/tests/data/need_id_refs/dummy_1.cpp",
 22                    "remote_url": "https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3",
 23                    "source_map": {
 24                        "start": {"row": 2, "column": 13},
 25                        "end": {"row": 2, "column": 51},
 26                    },
 27                    "tagged_scope": "void dummy_func1(){\n     //...\n }",
 28                    "need_ids": ["NEED_001", "NEED_002", "NEED_003", "NEED_004"],
 29                    "marker": "@need-ids:",
 30                    "type": "need-id-refs",
 31                },
 32            ],
 33            [
 34                ".. needextend:: NEED_001\n   :remote-url: https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3\n\n",
 35                ".. needextend:: NEED_002\n   :remote-url: https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3\n\n",
 36                ".. needextend:: NEED_003\n   :remote-url: https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3\n\n",
 37                ".. needextend:: NEED_004\n   :remote-url: https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3\n\n",
 38            ],
 39        ),
 40        (
 41            [
 42                {
 43                    "filepath": "/home/jui-wen/git_repo/ub/sphinx-codelinks/tests/data/need_id_refs/dummy_1.cpp",
 44                    "remote_url": "https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3",
 45                    "source_map": {
 46                        "start": {"row": 2, "column": 13},
 47                        "end": {"row": 2, "column": 51},
 48                    },
 49                    "tagged_scope": "void dummy_func1(){\n     //...\n }",
 50                    "need_ids": ["NEED_001"],
 51                    "marker": "@need-ids:",
 52                    "type": "need-id-refs",
 53                },
 54                {
 55                    "filepath": "/home/jui-wen/git_repo/ub/sphinx-codelinks/tests/data/need_id_refs/dummy_1.cpp",
 56                    "remote_url": "https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L10",
 57                    "source_map": {
 58                        "start": {"row": 2, "column": 13},
 59                        "end": {"row": 2, "column": 51},
 60                    },
 61                    "tagged_scope": "void dummy_func1(){\n     //...\n }",
 62                    "need_ids": ["NEED_001"],
 63                    "marker": "@need-ids:",
 64                    "type": "need-id-refs",
 65                },
 66            ],
 67            [
 68                ".. needextend:: NEED_001\n   :remote-url: https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L3,https://github.com/useblocks/sphinx-codelinks/blob/main/tests/data/need_id_refs/dummy_1.cpp#L10\n\n"
 69            ],
 70        ),
 71    ],
 72)
 73def test_convert_marked_content(markers, texts):
 74    # Normalize line endings
 75    texts = [line.replace("\r\n", "\n").replace("\r", "\n") for line in texts]
 76    needextend_texts, errors = convert_marked_content(markers)
 77
 78    assert not errors
 79
 80    assert needextend_texts == texts
 81
 82
 83def test_convert_marked_content_ignores_multiline_needs() -> None:
 84    """``write rst`` converts the references only: a multi-line need record is valid
 85    input and yields nothing."""
 86    markers = [
 87        {
 88            "filepath": "src/demo.cpp",
 89            "remote_url": None,
 90            "source_map": {
 91                "start": {"row": 0, "column": 3},
 92                "end": {"row": 5, "column": 11},
 93            },
 94            "tagged_scope": None,
 95            "type": "multiline-need",
 96            "need": {"type": "impl", "title": "Demo", "content": "", "doctype": ".rst"},
 97            "markup": "rst",
 98            "source": {"project": "p", "path": "src/demo.cpp", "root": "git"},
 99        },
100        {
101            "filepath": "src/demo.cpp",
102            "remote_url": "https://example.com/src/demo.cpp#L8",
103            "source_map": {
104                "start": {"row": 7, "column": 13},
105                "end": {"row": 7, "column": 21},
106            },
107            "tagged_scope": None,
108            "need_ids": ["NEED_001"],
109            "marker": "@need-ids:",
110            "type": "need-id-refs",
111        },
112    ]
113
114    needextend_texts, errors = convert_marked_content(markers)
115
116    assert not errors
117    assert needextend_texts == [
118        ".. needextend:: NEED_001\n   :remote-url: https://example.com/src/demo.cpp#L8\n\n"
119    ]
120
121
122def test_a_multiline_need_record_needs_its_need() -> None:
123    schema = MarkedContentSchema(
124        filepath="src/demo.cpp",
125        remote_url=None,
126        source_map={"start": {"row": 0, "column": 0}, "end": {"row": 1, "column": 0}},
127        tagged_scope=None,
128        type=MarkedContentType.multiline_need,
129    )
130
131    assert schema.check_conditional_required_fields() == [
132        "Need definition is required for marked content of type 'multiline-need'"
133    ]
134
135
136def test_convert_marked_content_reads_the_records_analyse_writes(
137    tmp_path: Path,
138) -> None:
139    """Every key ``codelinks analyse`` writes for a reference is one ``write rst``
140    accepts: a key the schema lacks makes it drop the record with an error. The records
141    carry the attached entity, ``scope``, since the analysis names it."""
142    source = tmp_path / "demo.cpp"
143    source.write_text(
144        "// @need-ids: NEED_001\nvoid demo() {}\n// @need-ids: NEED_002\n",
145        encoding="utf-8",
146    )
147    analyse = SourceAnalyse(
148        SourceAnalyseConfig(src_files=[source], src_dir=tmp_path, get_need_id_refs=True)
149    )
150    analyse.git_remote_url = None
151    analyse.git_commit_rev = None
152    analyse.run()
153    records = [item.to_dict() for item in analyse.all_marked_content]
154    assert [record["scope"] for record in records] == [
155        {
156            "kind": "function_definition",
157            "start": 2,
158            "end": 2,
159            "language": "cpp",
160            "entity_kind": "function",
161            "qualified_name": "demo",
162            "semantic": [{"kind": "function", "name": "demo"}],
163            "mechanism": "leading",
164        },
165        None,
166    ]
167    for record in records:
168        record["remote_url"] = (
169            f"https://example.com/demo.cpp#L{record['source_map']['start']['row'] + 1}"
170        )
171
172    needextend_texts, errors = convert_marked_content(records)
173
174    assert not errors
175    assert needextend_texts == [
176        ".. needextend:: NEED_001\n   :remote-url: https://example.com/demo.cpp#L1\n\n",
177        ".. needextend:: NEED_002\n   :remote-url: https://example.com/demo.cpp#L3\n\n",
178    ]