|
4 | 4 | from dataclasses import dataclass
|
5 | 5 | from itertools import chain
|
6 | 6 | from pathlib import Path
|
7 |
| -from typing import TYPE_CHECKING, Any, Iterator, List, Optional |
| 7 | +from typing import TYPE_CHECKING, Any, Iterator, List, Optional, cast |
8 | 8 |
|
9 | 9 | from ....jsonrpc2.protocol import rpc_method
|
10 | 10 | from ....utils.async_tools import run_coroutine_in_thread, run_in_thread
|
11 | 11 | from ....utils.logging import LoggingDescriptor
|
12 | 12 | from ....utils.uri import Uri
|
13 |
| -from ...common.lsp_types import Model, Position, Range, TextDocumentIdentifier |
| 13 | +from ...common.lsp_types import ( |
| 14 | + DocumentUri, |
| 15 | + Model, |
| 16 | + Position, |
| 17 | + Range, |
| 18 | + TextDocumentIdentifier, |
| 19 | +) |
| 20 | +from ..utils.async_ast import AsyncVisitor |
14 | 21 | from .protocol_part import RobotLanguageServerProtocolPart
|
15 | 22 |
|
16 | 23 | if TYPE_CHECKING:
|
@@ -39,21 +46,58 @@ class TestItem(Model):
|
39 | 46 | type: str
|
40 | 47 | id: str
|
41 | 48 | label: str
|
42 |
| - uri: Optional[str] = None |
| 49 | + uri: Optional[DocumentUri] = None |
43 | 50 | children: Optional[List[TestItem]] = None
|
44 | 51 | description: Optional[str] = None
|
45 | 52 | range: Optional[Range] = None
|
46 | 53 | tags: Optional[List[str]] = None
|
47 | 54 | error: Optional[str] = None
|
48 | 55 |
|
49 | 56 |
|
| 57 | +class FindTestCasesVisitor(AsyncVisitor): |
| 58 | + async def get(self, source: DocumentUri, model: ast.AST, id: Optional[str]) -> List[TestItem]: |
| 59 | + self._results: List[TestItem] = [] |
| 60 | + self.source = source |
| 61 | + self.id = id |
| 62 | + await self.visit(model) |
| 63 | + return self._results |
| 64 | + |
| 65 | + async def visit_Section(self, node: ast.AST) -> None: # noqa: N802 |
| 66 | + from robot.parsing.model.blocks import TestCaseSection |
| 67 | + |
| 68 | + if isinstance(node, TestCaseSection): |
| 69 | + await self.generic_visit(node) |
| 70 | + |
| 71 | + async def visit_TestCase(self, node: ast.AST) -> None: # noqa: N802 |
| 72 | + from robot.parsing.model.blocks import TestCase |
| 73 | + from robot.parsing.model.statements import Tags |
| 74 | + |
| 75 | + test_case = cast(TestCase, node) |
| 76 | + self._results.append( |
| 77 | + TestItem( |
| 78 | + type="test", |
| 79 | + id=f"{self.id}.{test_case.name}" if self.id else test_case.name, |
| 80 | + label=test_case.name, |
| 81 | + uri=self.source, |
| 82 | + range=Range( |
| 83 | + start=Position(line=test_case.lineno - 1, character=test_case.col_offset), |
| 84 | + end=Position( |
| 85 | + line=(test_case.end_lineno if test_case.end_lineno != -1 else test_case.lineno) - 1, |
| 86 | + character=test_case.end_col_offset if test_case.end_col_offset != -1 else test_case.col_offset, |
| 87 | + ), |
| 88 | + ), |
| 89 | + tags=[str(tag) for tag in chain(*[tags.values for tags in test_case.body if isinstance(tags, Tags)])], |
| 90 | + ) |
| 91 | + ) |
| 92 | + |
| 93 | + |
50 | 94 | class DiscoveringProtocolPart(RobotLanguageServerProtocolPart):
|
51 | 95 | _logger = LoggingDescriptor()
|
52 | 96 |
|
53 | 97 | def __init__(self, parent: RobotLanguageServerProtocol) -> None:
|
54 | 98 | super().__init__(parent)
|
55 | 99 |
|
56 |
| - def get_tests_from_workspace_threading(self, workspace_folder: Path, paths: Optional[List[str]]) -> List[TestItem]: |
| 100 | + def _get_tests_from_workspace(self, workspace_folder: Path, paths: Optional[List[str]]) -> List[TestItem]: |
57 | 101 | from robot.output.logger import LOGGER
|
58 | 102 | from robot.running import TestCase, TestSuite
|
59 | 103 |
|
@@ -152,55 +196,30 @@ def nonexisting_paths(paths: List[str]) -> Iterator[str]:
|
152 | 196 | return [TestItem(type="error", id=Path.cwd().name, label=Path.cwd().name, error=str(e))]
|
153 | 197 |
|
154 | 198 | @rpc_method(name="robot/discovering/getTestsFromWorkspace", param_type=GetAllTestsParams)
|
| 199 | + @_logger.call(entering=True, exiting=True, exception=True) |
155 | 200 | async def get_tests_from_workspace(
|
156 | 201 | self,
|
157 | 202 | workspace_folder: str,
|
158 | 203 | paths: Optional[List[str]],
|
159 | 204 | *args: Any,
|
160 | 205 | **kwargs: Any,
|
161 | 206 | ) -> List[TestItem]:
|
162 |
| - return await run_in_thread(self.get_tests_from_workspace_threading, Uri(workspace_folder).to_path(), paths) |
163 |
| - |
164 |
| - def get_tests_from_document_threading( |
165 |
| - self, text_document: TextDocumentIdentifier, id: Optional[str], model: ast.AST |
166 |
| - ) -> List[TestItem]: |
167 |
| - from robot.parsing.model.blocks import TestCase |
168 |
| - from robot.parsing.model.statements import Tags |
169 |
| - |
170 |
| - return [ |
171 |
| - TestItem( |
172 |
| - type="test", |
173 |
| - id=f"{id}.{test_case.name}" if id else test_case.name, |
174 |
| - label=test_case.name, |
175 |
| - uri=text_document.uri, |
176 |
| - range=Range( |
177 |
| - start=Position(line=test_case.lineno - 1, character=test_case.col_offset), |
178 |
| - end=Position( |
179 |
| - line=(test_case.end_lineno if test_case.end_lineno != -1 else test_case.lineno) - 1, |
180 |
| - character=test_case.end_col_offset if test_case.end_col_offset != -1 else test_case.col_offset, |
181 |
| - ), |
182 |
| - ), |
183 |
| - tags=[ |
184 |
| - str(tag) for tag in chain(*[tags.values for tags in ast.walk(test_case) if isinstance(tags, Tags)]) |
185 |
| - ], |
186 |
| - ) |
187 |
| - for test_case in ast.walk(model) |
188 |
| - if isinstance(test_case, TestCase) |
189 |
| - ] |
| 207 | + return await run_in_thread(self._get_tests_from_workspace, Uri(workspace_folder).to_path(), paths) |
190 | 208 |
|
191 | 209 | @rpc_method(name="robot/discovering/getTestsFromDocument", param_type=GetTestsParams)
|
| 210 | + @_logger.call(entering=True, exiting=True, exception=True) |
192 | 211 | async def get_tests_from_document(
|
193 | 212 | self, text_document: TextDocumentIdentifier, id: Optional[str], *args: Any, **kwargs: Any
|
194 | 213 | ) -> List[TestItem]:
|
195 | 214 | async def run() -> List[TestItem]:
|
196 |
| - return self.get_tests_from_document_threading( |
197 |
| - text_document, |
198 |
| - id, |
| 215 | + return await FindTestCasesVisitor().get( |
| 216 | + text_document.uri, |
199 | 217 | await self.parent.documents_cache.get_model(
|
200 | 218 | await self.parent.robot_workspace.get_or_open_document(
|
201 | 219 | Uri(text_document.uri).to_path(), language_id="robotframework"
|
202 | 220 | )
|
203 | 221 | ),
|
| 222 | + id, |
204 | 223 | )
|
205 | 224 |
|
206 | 225 | return await run_coroutine_in_thread(run)
|
0 commit comments