Skip to content

Commit 607cf8d

Browse files
committed
feat: support for new RF 6.1 --parse-include option for discovering and executing tests
1 parent 457f3ae commit 607cf8d

File tree

13 files changed

+195
-45
lines changed

13 files changed

+195
-45
lines changed

.vscode/launch.json

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,7 @@
4646
".",
4747
//"config", "info", "desc",
4848
"discover",
49-
// "all",
50-
// "--norpa",
51-
// "--by-longname", "asd:dasd",
52-
// "--suite",
53-
// "Data.Tests.Versions.Rf61.a suite with a custom Name"
54-
// "Robotframework.Atest.Testdata.Variables",
55-
//"."
56-
"all",
57-
"--suite",
58-
"Data.Tests.Versions.Rf61",
59-
"."
49+
"info"
6050
]
6151
},
6252
{

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ async def _analyze_keyword_call(
438438
code="ReservedKeyword",
439439
)
440440

441-
if get_robot_version() >= (6, 0, 0) and result.is_resource_keyword and result.is_private():
441+
if get_robot_version() >= (6, 0) and result.is_resource_keyword and result.is_private():
442442
if self.namespace.source != result.source:
443443
self.append_diagnostics(
444444
range=kw_range,

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/library_doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def normalized_tags(self) -> List[str]:
387387

388388
@single_call
389389
def is_private(self) -> bool:
390-
if get_robot_version() < (6, 0, 0):
390+
if get_robot_version() < (6, 0):
391391
return False
392392

393393
return "robot:private" in self.normalized_tags()

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/namespace.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,7 +1769,7 @@ def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
17691769
return result
17701770

17711771
def _get_keyword_from_self(self, name: str) -> Optional[KeywordDoc]:
1772-
if get_robot_version() >= (6, 0, 0):
1772+
if get_robot_version() >= (6, 0):
17731773
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
17741774
(None, v) for v in self.self_library_doc.keywords.get_all(name)
17751775
]
@@ -1813,7 +1813,7 @@ def _get_explicit_keyword(self, name: str) -> Optional[KeywordDoc]:
18131813
for owner_name, kw_name in self._yield_owner_and_kw_names(name):
18141814
found.extend(self.find_keywords(owner_name, kw_name))
18151815

1816-
if get_robot_version() >= (6, 0, 0) and len(found) > 1:
1816+
if get_robot_version() >= (6, 0) and len(found) > 1:
18171817
found = self._select_best_matches(found)
18181818

18191819
if len(found) > 1:
@@ -1832,7 +1832,7 @@ def find_keywords(self, owner_name: str, name: str) -> List[Tuple[LibraryEntry,
18321832
if self._all_keywords is None:
18331833
self._all_keywords = list(chain(self.namespace._libraries.values(), self.namespace._resources.values()))
18341834

1835-
if get_robot_version() >= (6, 0, 0):
1835+
if get_robot_version() >= (6, 0):
18361836
result: List[Tuple[LibraryEntry, KeywordDoc]] = []
18371837
for v in self._all_keywords:
18381838
if eq(v.alias or v.name, owner_name):
@@ -1914,7 +1914,7 @@ def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
19141914
if self._resource_keywords is None:
19151915
self._resource_keywords = list(chain(self.namespace._resources.values()))
19161916

1917-
if get_robot_version() >= (6, 0, 0):
1917+
if get_robot_version() >= (6, 0):
19181918
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
19191919
for v in self._resource_keywords:
19201920
r = v.library_doc.keywords.get_all(name)
@@ -1930,7 +1930,7 @@ def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
19301930
if not found:
19311931
return None
19321932

1933-
if get_robot_version() >= (6, 0, 0):
1933+
if get_robot_version() >= (6, 0):
19341934
if len(found) > 1:
19351935
found = self._prioritize_same_file_or_public(found)
19361936

@@ -1970,7 +1970,7 @@ def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
19701970
if self._library_keywords is None:
19711971
self._library_keywords = list(chain(self.namespace._libraries.values()))
19721972

1973-
if get_robot_version() >= (6, 0, 0):
1973+
if get_robot_version() >= (6, 0):
19741974
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
19751975
for v in self._library_keywords:
19761976
r = v.library_doc.keywords.get_all(name)
@@ -1987,7 +1987,7 @@ def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
19871987
if not found:
19881988
return None
19891989

1990-
if get_robot_version() >= (6, 0, 0):
1990+
if get_robot_version() >= (6, 0):
19911991
if len(found) > 1:
19921992
found = self._select_best_matches(found)
19931993
if len(found) > 1:

packages/runner/src/robotcode/runner/cli/discover/discover.py

Lines changed: 124 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import platform
23
import re
34
import sys
45
from collections import defaultdict
@@ -45,7 +46,7 @@ def _patch() -> None:
4546
__patched = True
4647

4748
if get_robot_version() <= (6, 1):
48-
if get_robot_version() > (5, 0) and get_robot_version() < (6, 0, 0) or get_robot_version() < (5, 0):
49+
if get_robot_version() > (5, 0) and get_robot_version() < (6, 0) or get_robot_version() < (5, 0):
4950
from robot.running.builder.testsettings import TestDefaults # pyright: ignore[reportMissingImports]
5051
else:
5152
from robot.running.builder.settings import Defaults as TestDefaults # pyright: ignore[reportMissingImports]
@@ -82,6 +83,16 @@ def build_suite(self: SuiteStructureParser, structure: Any) -> Tuple[TestSuite,
8283

8384
SuiteStructureParser._build_suite = build_suite
8485

86+
old_validate_execution_mode = SuiteStructureParser._validate_execution_mode
87+
88+
def _validate_execution_mode(self: SuiteStructureParser, suite: TestSuite) -> None:
89+
try:
90+
old_validate_execution_mode(self, suite)
91+
except DataError as e:
92+
LOGGER.error(f"Parsing '{suite.source}' failed: {e.message}")
93+
94+
SuiteStructureParser._validate_execution_mode = _validate_execution_mode
95+
8596
elif get_robot_version() >= (6, 1):
8697
from robot.parsing.suitestructure import SuiteDirectory, SuiteFile
8798
from robot.running.builder.settings import TestDefaults # pyright: ignore[reportMissingImports]
@@ -124,6 +135,16 @@ def build_suite_directory(
124135

125136
SuiteStructureParser._build_suite_directory = build_suite_directory
126137

138+
old_validate_execution_mode = SuiteStructureParser._validate_execution_mode
139+
140+
def _validate_execution_mode(self: SuiteStructureParser, suite: TestSuite) -> None:
141+
try:
142+
old_validate_execution_mode(self, suite)
143+
except DataError as e:
144+
LOGGER.error(f"Parsing '{suite.source}' failed: {e.message}")
145+
146+
SuiteStructureParser._validate_execution_mode = _validate_execution_mode
147+
127148
old_get_file = FileReader._get_file
128149

129150
def get_file(self: FileReader, source: Union[str, Path, IOBase], accept_text: bool) -> Any:
@@ -145,6 +166,8 @@ class TestItem:
145166
name: str
146167
longname: str
147168
uri: Optional[DocumentUri] = None
169+
rel_source: Optional[str] = None
170+
needs_parse_include: bool = False
148171
children: Optional[List["TestItem"]] = None
149172
description: Optional[str] = None
150173
range: Optional[Range] = None
@@ -165,15 +188,25 @@ class Statistics:
165188
tests: int = 0
166189

167190

191+
def get_rel_source(source: Optional[str]) -> Optional[str]:
192+
if source is None:
193+
return None
194+
try:
195+
return str(Path(source).relative_to(Path.cwd()).as_posix())
196+
except ValueError:
197+
return str(source)
198+
199+
168200
class Collector(SuiteVisitor):
169201
def __init__(self) -> None:
170202
super().__init__()
171203
self.all: TestItem = TestItem(
172204
type="workspace",
173-
id=str(Path.cwd().absolute()),
205+
id=str(Path.cwd().resolve()),
174206
name=Path.cwd().name,
175207
longname=Path.cwd().name,
176208
uri=str(Uri.from_path(Path.cwd())),
209+
needs_parse_include=get_robot_version() >= (6, 1),
177210
)
178211
self._current = self.all
179212
self.suites: List[TestItem] = []
@@ -185,10 +218,11 @@ def visit_suite(self, suite: TestSuite) -> None:
185218
try:
186219
item = TestItem(
187220
type="suite",
188-
id=f"{Path(suite.source).absolute() if suite.source is not None else ''};{suite.longname}",
221+
id=f"{Path(suite.source).resolve() if suite.source is not None else ''};{suite.longname}",
189222
name=suite.name,
190223
longname=suite.longname,
191-
uri=str(Uri.from_path(Path(suite.source).absolute())) if suite.source else None,
224+
uri=str(Uri.from_path(Path(suite.source).resolve())) if suite.source else None,
225+
rel_source=get_rel_source(suite.source),
192226
range=Range(
193227
start=Position(line=0, character=0),
194228
end=Position(line=0, character=0),
@@ -224,10 +258,11 @@ def visit_test(self, test: TestCase) -> None:
224258
try:
225259
item = TestItem(
226260
type="test",
227-
id=f"{Path(test.source).absolute() if test.source is not None else ''};{test.longname};{test.lineno}",
261+
id=f"{Path(test.source).resolve() if test.source is not None else ''};{test.longname};{test.lineno}",
228262
name=test.name,
229263
longname=test.longname,
230-
uri=str(Uri.from_path(Path(test.source).absolute())) if test.source else None,
264+
uri=str(Uri.from_path(Path(test.source).resolve())) if test.source else None,
265+
rel_source=get_rel_source(test.source),
231266
range=Range(
232267
start=Position(line=test.lineno - 1, character=0),
233268
end=Position(line=test.lineno - 1, character=0),
@@ -287,7 +322,7 @@ def build_diagnostics(messages: List[Message]) -> Dict[str, List[Diagnostic]]:
287322
def add_diagnostic(
288323
message: Message, source_uri: Optional[str] = None, line: Optional[int] = None, text: Optional[str] = None
289324
) -> None:
290-
source_uri = str(Uri.from_path(Path(source_uri).absolute() if source_uri else Path.cwd()))
325+
source_uri = str(Uri.from_path(Path(source_uri).resolve() if source_uri else Path.cwd()))
291326

292327
if source_uri not in result:
293328
result[source_uri] = []
@@ -360,7 +395,7 @@ def handle_options(
360395
lang=settings.languages,
361396
allow_empty_suite=settings.run_empty_suite,
362397
)
363-
elif get_robot_version() >= (6, 0, 0):
398+
elif get_robot_version() >= (6, 0):
364399
builder = TestSuiteBuilder(
365400
settings["SuiteNames"],
366401
included_extensions=settings.extension,
@@ -583,7 +618,7 @@ def tags(
583618
```
584619
"""
585620

586-
suite, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
621+
suite, _diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
587622

588623
collector = Collector()
589624
suite.visit(collector)
@@ -594,16 +629,89 @@ def tags(
594629
def print(tags: Dict[str, List[TestItem]]) -> Iterable[str]:
595630
for tag, items in tags.items():
596631
yield f"{tag}{os.linesep}"
597-
# for item in items:
598-
# yield f" {item.longname}{os.linesep}"
599-
# if item.uri:
600-
# yield (
601-
# f" ({Uri(item.uri).to_path()}{f':{item.range.start.line+1}' if item.range else ''})"
602-
# f"{os.linesep}"
603-
# )
604632

605633
if collector.suites:
606634
app.echo_via_pager(print(collector.tags))
607635

608636
else:
609637
app.print_data(TagsResult(collector.tags), remove_defaults=True)
638+
639+
640+
@dataclass
641+
class RobotVersion:
642+
major: int
643+
minor: int
644+
patch: Optional[int] = None
645+
pre_id: Optional[str] = None
646+
pre_number: Optional[int] = None
647+
dev: Optional[int] = None
648+
649+
650+
@dataclass
651+
class PythonVersion:
652+
major: int
653+
minor: int
654+
micro: int
655+
releaselevel: str
656+
serial: int
657+
658+
659+
@dataclass
660+
class Info:
661+
robot_version: RobotVersion
662+
robot_version_string: str
663+
robot_env: Dict[str, str]
664+
python_version: PythonVersion
665+
python_version_string: str
666+
machine: str
667+
processor: str
668+
platform: str
669+
system: str
670+
system_version: str
671+
672+
673+
@discover.command(
674+
add_help_option=True,
675+
)
676+
@pass_application
677+
def info(
678+
app: Application,
679+
) -> None:
680+
"""\
681+
Shows some informations about the current *robot* environment.
682+
683+
\b
684+
Examples:
685+
```
686+
robotcode discover info
687+
```
688+
"""
689+
from robot.version import get_version as get_version
690+
691+
robot_env: Dict[str, str] = {}
692+
if "ROBOT_OPTIONS" in os.environ:
693+
robot_env["ROBOT_OPTIONS"] = os.environ["ROBOT_OPTIONS"]
694+
if "ROBOT_SYSLOG_FILE" in os.environ:
695+
robot_env["ROBOT_SYSLOG_FILE"] = os.environ["ROBOT_SYSLOG_FILE"]
696+
if "ROBOT_SYSLOG_LEVEL" in os.environ:
697+
robot_env["ROBOT_SYSLOG_LEVEL"] = os.environ["ROBOT_SYSLOG_LEVEL"]
698+
if "ROBOT_INTERNAL_TRACES" in os.environ:
699+
robot_env["ROBOT_INTERNAL_TRACES"] = os.environ["ROBOT_INTERNAL_TRACES"]
700+
701+
info = Info(
702+
RobotVersion(*get_robot_version()),
703+
get_version(),
704+
robot_env,
705+
PythonVersion(*sys.version_info),
706+
platform.python_version(),
707+
platform.machine(),
708+
platform.processor(),
709+
sys.platform,
710+
platform.system(),
711+
platform.version(),
712+
)
713+
714+
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
715+
app.print_data(info, remove_defaults=True)
716+
else:
717+
app.print_data(info, remove_defaults=True)

packages/runner/src/robotcode/runner/cli/libdoc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def main(self, arguments: Any, **options: Any) -> Any:
5151
@click.version_option(
5252
version=__version__,
5353
package_name="robotcode.runner.libdoc",
54-
prog_name="RobotCode LibDoc",
54+
prog_name="RobotCode libdoc",
5555
message=f"%(prog)s %(version)s\n{USAGE.splitlines()[0].split(' -- ')[0].strip()} {get_full_version()}",
5656
)
5757
@click.argument("robot_options_and_args", nargs=-1, type=click.Path())

packages/runner/src/robotcode/runner/cli/rebot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ def main(self, arguments: Any, **options: Any) -> Any:
5050
)
5151
@click.version_option(
5252
version=__version__,
53-
package_name="robotcode.runner.libdoc",
54-
prog_name="RobotCode LibDoc",
53+
package_name="robotcode.runner.rebot",
54+
prog_name="RobotCode rebot",
5555
message=f"%(prog)s %(version)s\n{USAGE.splitlines()[0].split(' -- ')[0].strip()} {get_full_version()}",
5656
)
5757
@click.argument("robot_options_and_args", nargs=-1, type=click.Path())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*** Tasks ***
2+
first task
3+
Fatal Error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*** Settings ***
2+
Name A Suite with Name setting in __init__.robot
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*** Settings ***
2+
3+
*** Test Cases ***
4+
first
5+
No Operation

0 commit comments

Comments
 (0)