-
-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathconftest.py
More file actions
144 lines (110 loc) · 5.16 KB
/
Copy pathconftest.py
File metadata and controls
144 lines (110 loc) · 5.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""Shared fixtures for the robotcode test suite.
Fixtures defined here are available to ALL tests under `tests/robotcode/*`
through pytest's conftest.py inheritance. Sub-directories (e.g.
`test_semantic_analyzer/`, `language_server/robotframework/parts/`) can
extend this with their own conftest.py files for module-specific setup.
The fixtures here cover the most common SemanticAnalyzer test setup so
that individual test files don't need to re-implement parsing, finder
mocks, or analyzer bootstrapping.
"""
import io
from ast import AST
from typing import Any, Callable, Optional
import pytest
from pytest_mock import MockerFixture
from robot.api import get_model
from robotcode.robot.diagnostics.analyzer_result import AnalyzerResult
from robotcode.robot.diagnostics.import_resolver import ResolvedImports
from robotcode.robot.diagnostics.keyword_finder import KeywordFinder
from robotcode.robot.diagnostics.library_doc import KeywordDoc, ResourceDoc
from robotcode.robot.diagnostics.semantic_analyzer.analyzer import (
SemanticAnalyzer,
_get_builtin_variables,
)
from robotcode.robot.diagnostics.variable_scope import VariableScope
# --------------------------------------------------------------------------
# Plain factories (no mocks, no fixtures needed — re-exported for
# convenience). Importable from tests under any sub-directory.
# --------------------------------------------------------------------------
def parse_robot(text: str) -> AST:
"""Parse Robot Framework text into an AST model."""
return get_model(io.StringIO(text)) # type: ignore[no-any-return]
def make_resource_doc(source: str = "/test.robot") -> ResourceDoc:
"""Build a minimal ResourceDoc for analyzer setup."""
return ResourceDoc(name="test", source=source)
# --------------------------------------------------------------------------
# Mock-builder fixtures (`mocker` is a pytest-mock fixture).
# --------------------------------------------------------------------------
@pytest.fixture
def make_finder(mocker: MockerFixture) -> Callable[..., KeywordFinder]:
"""Factory: build a `KeywordFinder` mock that resolves names from an
optional `keyword_map` (`name -> KeywordDoc | None`).
The mock uses `mocker.create_autospec` so it validates against the real
interface. By default it returns `None` for any lookup.
Examples:
finder = make_finder() # always None
finder = make_finder({"My Keyword": kw_doc}) # resolves My Keyword
"""
def factory(keyword_map: Optional[dict[str, Optional[KeywordDoc]]] = None) -> KeywordFinder:
finder = mocker.create_autospec(KeywordFinder, instance=True)
finder.result_bdd_prefix = None
finder.multiple_keywords_result = None
finder.diagnostics = []
kw_map = keyword_map or {}
def find_keyword(name: str, raise_keyword_error: bool = True) -> Optional[KeywordDoc]:
return kw_map.get(name)
finder.find_keyword.side_effect = find_keyword
return finder
return factory
@pytest.fixture
def analyzer_factory(
mocker: MockerFixture,
make_finder: Callable[..., KeywordFinder],
) -> Callable[..., AnalyzerResult]:
"""Factory: parse text, set up a SemanticAnalyzer with the supplied
keyword map, run it, and return the AnalyzerResult.
Bypasses `analyzer.resolve()` (no real ImportsManager needed) by setting
the relevant internal state directly. Use this fixture for the common
"run the analyzer on this text and inspect the result" case; for more
elaborate setup (e.g. pre-resolved imports with errors), build the
analyzer manually using `parse_robot` + `make_resource_doc`.
Examples:
result = analyzer_factory("*** Test Cases ***\\nT\\n Log hi\\n")
result = analyzer_factory(text, keyword_map={"My KW": kw_doc})
"""
def factory(
text: str,
keyword_map: Optional[dict[str, Optional[KeywordDoc]]] = None,
source: str = "/test.robot",
library_doc: Optional[Any] = None,
) -> AnalyzerResult:
model = parse_robot(text)
analyzer = SemanticAnalyzer(model, source, f"file://{source}")
if library_doc is None:
analyzer._library_doc = make_resource_doc(source)
else:
analyzer._library_doc = library_doc
analyzer._variable_scope = VariableScope(
command_line=[],
own=[],
builtin=_get_builtin_variables(),
)
analyzer._resolved_imports = ResolvedImports()
finder = make_finder(keyword_map)
return analyzer.run(finder)
return factory
@pytest.fixture
def make_library_doc_mock(mocker: MockerFixture) -> Callable[..., Any]:
"""Factory: build a `_library_doc`-shaped mock with `resource_variables`
/ `resource_imports` accessors. Useful when a real `ResourceDoc` would
be over-the-top.
"""
def factory(
resource_variables: Optional[list[Any]] = None,
resource_imports: Optional[list[Any]] = None,
) -> Any:
doc = mocker.MagicMock()
doc.resource_variables = resource_variables or []
doc.resource_imports = resource_imports or []
return doc
return factory