Skip to content

Commit 320f947

Browse files
committed
add dryrun debug option and test explorer profile closes #25
1 parent 0c7dbfd commit 320f947

File tree

6 files changed

+95
-26
lines changed

6 files changed

+95
-26
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,11 @@
528528
"description": "Where to launch the debug target: internal console, integrated terminal, or external terminal.",
529529
"default": "integratedTerminal"
530530
},
531+
"dryRun": {
532+
"type": "boolean",
533+
"description": "Verifies test data and runs tests so that library keywords are not executed. Corresponds to the '--dryrun' option of the robot module.",
534+
"default": false
535+
},
531536
"python": {
532537
"type": "string",
533538
"description": "Specifies the python interpreter to use. If not specified, the python interpreter defined for the extension is used.",

robotcode/debugger/launcher/server.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ async def _launch(
141141
groupOutput: Optional[bool] = False,
142142
stopOnEntry: Optional[bool] = False, # noqa: N803
143143
arguments: Optional[LaunchRequestArguments] = None,
144+
dryRun: Optional[bool] = None,
144145
*_args: Any,
145146
**_kwargs: Any,
146147
) -> None:
@@ -179,6 +180,9 @@ async def _launch(
179180

180181
run_args += ["--"]
181182

183+
if dryRun:
184+
run_args += ["--dryrun"]
185+
182186
if outputDir:
183187
run_args += ["-d", outputDir]
184188

robotcode/debugger/listeners.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import dataclass
2+
from pathlib import Path
23
from typing import Any, Dict, Iterator, List, Optional, Union, cast
34

45
from .dap_types import Event, Model
@@ -8,6 +9,7 @@
89
@dataclass
910
class RobotExecutionEventBody(Model):
1011
type: str
12+
id: str
1113
attributes: Optional[Dict[str, Any]] = None
1214
failed_keywords: Optional[List[Dict[str, Any]]] = None
1315

@@ -23,7 +25,15 @@ def __init__(self, no_debug: bool = False) -> None:
2325

2426
def start_suite(self, name: str, attributes: Dict[str, Any]) -> None:
2527
Debugger.instance().send_event(
26-
self, Event(event="robotStarted", body=RobotExecutionEventBody(type="suite", attributes=dict(attributes)))
28+
self,
29+
Event(
30+
event="robotStarted",
31+
body=RobotExecutionEventBody(
32+
type="suite",
33+
id=f"{attributes.get('source', '')};{attributes.get('longname', '')}",
34+
attributes=dict(attributes),
35+
),
36+
),
2737
)
2838

2939
Debugger.instance().start_output_group(name, attributes, "SUITE")
@@ -41,7 +51,10 @@ def end_suite(self, name: str, attributes: Dict[str, Any]) -> None:
4151
Event(
4252
event="robotEnded",
4353
body=RobotExecutionEventBody(
44-
type="suite", attributes=dict(attributes), failed_keywords=self.failed_keywords
54+
type="suite",
55+
attributes=dict(attributes),
56+
id=f"{attributes.get('source', '')};{attributes.get('longname', '')}",
57+
failed_keywords=self.failed_keywords,
4558
),
4659
),
4760
)
@@ -50,7 +63,16 @@ def start_test(self, name: str, attributes: Dict[str, Any]) -> None:
5063
self.failed_keywords = None
5164

5265
Debugger.instance().send_event(
53-
self, Event(event="robotStarted", body=RobotExecutionEventBody(type="test", attributes=dict(attributes)))
66+
self,
67+
Event(
68+
event="robotStarted",
69+
body=RobotExecutionEventBody(
70+
type="test",
71+
id=f"{attributes.get('source', '')};{attributes.get('longname', '')};"
72+
f"{attributes.get('lineno', 0)}",
73+
attributes=dict(attributes),
74+
),
75+
),
5476
)
5577

5678
Debugger.instance().start_output_group(name, attributes, "TEST")
@@ -67,7 +89,11 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None:
6789
Event(
6890
event="robotEnded",
6991
body=RobotExecutionEventBody(
70-
type="test", attributes=dict(attributes), failed_keywords=self.failed_keywords
92+
type="test",
93+
id=f"{attributes.get('source', '')};{attributes.get('longname', '')};"
94+
f"{attributes.get('lineno', 0)}",
95+
attributes=dict(attributes),
96+
failed_keywords=self.failed_keywords,
7197
),
7298
),
7399
)
@@ -153,6 +179,9 @@ def close(self) -> None:
153179
class ListenerV3:
154180
ROBOT_LISTENER_API_VERSION = "3"
155181

182+
def __init__(self) -> None:
183+
self._event_sended = False
184+
156185
def start_suite(self, data: Any, result: Any) -> None:
157186
from robot.running import TestCase, TestSuite
158187

@@ -163,9 +192,15 @@ def enqueue(item: Union[TestSuite, TestCase]) -> Iterator[str]:
163192
for s in item.tests:
164193
yield from enqueue(s)
165194

166-
yield item.longname
195+
yield f"{Path(item.source).resolve()};{item.longname}"
196+
return
167197

168-
items = [i for i in enqueue(cast(TestSuite, data))]
198+
yield f"{Path(item.source).resolve()};{item.longname};{item.lineno}"
199+
200+
if self._event_sended:
201+
return
202+
203+
items = list(reversed([i for i in enqueue(cast(TestSuite, data))]))
169204

170205
Debugger.instance().send_event(
171206
self,
@@ -175,6 +210,8 @@ def enqueue(item: Union[TestSuite, TestCase]) -> Iterator[str]:
175210
),
176211
)
177212

213+
self._event_sended = True
214+
178215
def end_suite(self, data: Any, result: Any) -> None:
179216
pass
180217

robotcode/language_server/robotframework/parts/discovering.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class FindTestCasesVisitor(AsyncVisitor):
6161
async def get(self, source: DocumentUri, model: ast.AST, base_name: Optional[str]) -> List[TestItem]:
6262
self._results: List[TestItem] = []
6363
self.source = source
64+
self.path = Uri(source).to_path().resolve()
6465
self.base_name = base_name
6566
await self.visit(model)
6667
return self._results
@@ -80,7 +81,7 @@ async def visit_TestCase(self, node: ast.AST) -> None: # noqa: N802
8081
self._results.append(
8182
TestItem(
8283
type="test",
83-
id=f"{self.source};{longname};{test_case.lineno}",
84+
id=f"{self.path};{longname};{test_case.lineno}",
8485
longname=longname,
8586
label=test_case.name,
8687
uri=self.source,
@@ -206,14 +207,13 @@ def generate(suite: TestSuite) -> TestItem:
206207

207208
test: TestCase
208209
for test in suite.tests:
209-
uri = str(Uri.from_path(test.source)) if test.source else None
210210
children.append(
211211
TestItem(
212212
type="test",
213-
id=f"{uri};{test.longname};{test.lineno}",
213+
id=f"{Path(test.source).resolve()};{test.longname};{test.lineno}",
214214
label=test.name,
215215
longname=test.longname,
216-
uri=uri,
216+
uri=str(Uri.from_path(test.source)) if test.source else None,
217217
range=Range(
218218
start=Position(line=test.lineno - 1, character=0),
219219
end=Position(line=test.lineno - 1, character=0),
@@ -225,13 +225,12 @@ def generate(suite: TestSuite) -> TestItem:
225225
for s in suite.suites:
226226
children.append(generate(s))
227227

228-
uri = str(Uri.from_path(suite.source)) if suite.source else None
229228
return TestItem(
230229
type="suite",
231-
id=f"{uri};{suite.longname}",
230+
id=f"{Path(suite.source).resolve()};{suite.longname}",
232231
label=suite.name,
233232
longname=suite.longname,
234-
uri=uri,
233+
uri=str(Uri.from_path(suite.source)) if suite.source else None,
235234
children=children,
236235
range=Range(
237236
start=Position(line=0, character=0),
@@ -277,20 +276,19 @@ def nonexisting_paths(paths: List[str]) -> Iterator[str]:
277276
)
278277
suite_item = [generate(suite)] if suite else []
279278

280-
uri = str(Uri.from_path(Path.cwd()))
281279
return [
282280
TestItem(
283281
type="workspace",
284-
id=uri,
282+
id=str(Path.cwd()),
285283
label=Path.cwd().name,
286284
longname=Path.cwd().name,
287-
uri=uri,
285+
uri=str(Uri.from_path(Path.cwd())),
288286
children=[
289287
*suite_item,
290288
*[
291289
TestItem(
292290
type="error",
293-
id=f"{Uri.from_path(i)};ERROR",
291+
id=f"{i};ERROR",
294292
longname="error",
295293
label=i,
296294
error=f"Parsing '{i}' failed: File or directory to does not exist.",

vscode-client/debugmanager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,8 @@ export class DebugManager {
338338
included: string[],
339339
excluded: string[],
340340
runId?: string,
341-
options?: vscode.DebugSessionOptions
341+
options?: vscode.DebugSessionOptions,
342+
dryRun?: boolean
342343
): Promise<void> {
343344
const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder);
344345

@@ -379,6 +380,7 @@ export class DebugManager {
379380
args: args,
380381
console: config.get("debug.defaultConsole", "integratedTerminal"),
381382
runId: runId,
383+
dryRun,
382384
},
383385
},
384386
options

vscode-client/testcontrollermanager.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type RobotEventType = "suite" | "test" | "keyword" | string;
2626

2727
interface RobotExecutionEvent {
2828
type: RobotEventType;
29+
id: string;
2930
attributes: RobotExecutionAttributes | undefined;
3031
failedKeywords: RobotExecutionAttributes[] | undefined;
3132
}
@@ -77,6 +78,8 @@ export class TestControllerManager {
7778
public readonly testController: vscode.TestController;
7879
public readonly runProfile: vscode.TestRunProfile;
7980
public readonly debugProfile: vscode.TestRunProfile;
81+
public readonly dryRunProfile: vscode.TestRunProfile;
82+
public readonly dryRunDebugProfile: vscode.TestRunProfile;
8083
private readonly refreshMutex = new Mutex();
8184
private readonly debugSessions = new Set<vscode.DebugSession>();
8285
private readonly didChangedTimer = new Map<string, DidChangeEntry>();
@@ -91,15 +94,31 @@ export class TestControllerManager {
9194
this.testController = vscode.tests.createTestController("robotCode.RobotFramework", "RobotFramework");
9295

9396
this.runProfile = this.testController.createRunProfile(
94-
"Run Tests",
97+
"Run",
9598
vscode.TestRunProfileKind.Run,
96-
async (request, token) => this.runTests(request, token)
99+
async (request, token) => this.runTests(request, token),
100+
true
101+
);
102+
103+
this.dryRunProfile = this.testController.createRunProfile(
104+
"Dry Run",
105+
vscode.TestRunProfileKind.Run,
106+
async (request, token) => this.runTests(request, token, true),
107+
false
97108
);
98109

99110
this.debugProfile = this.testController.createRunProfile(
100111
"Debug",
101112
vscode.TestRunProfileKind.Debug,
102-
async (request, token) => this.runTests(request, token)
113+
async (request, token) => this.runTests(request, token),
114+
true
115+
);
116+
117+
this.dryRunDebugProfile = this.testController.createRunProfile(
118+
"Dry Debug",
119+
vscode.TestRunProfileKind.Debug,
120+
async (request, token) => this.runTests(request, token, true),
121+
false
103122
);
104123

105124
this.testController.resolveHandler = async (item) => {
@@ -563,7 +582,11 @@ export class TestControllerManager {
563582

564583
private static readonly runId = TestControllerManager.runIdGenerator();
565584

566-
public async runTests(request: vscode.TestRunRequest, token: vscode.CancellationToken): Promise<void> {
585+
public async runTests(
586+
request: vscode.TestRunRequest,
587+
token: vscode.CancellationToken,
588+
dryRun?: boolean
589+
): Promise<void> {
567590
let includedItems: vscode.TestItem[] = [];
568591

569592
if (request.include) {
@@ -601,7 +624,7 @@ export class TestControllerManager {
601624
const workspaceItem = this.findTestItemByUri(workspace.uri.toString());
602625

603626
if (includedItems.length === 1 && includedItems[0] === workspaceItem && excluded.size === 0) {
604-
await DebugManager.runTests(workspace, [], [], [], runId, options);
627+
await DebugManager.runTests(workspace, [], [], [], runId, options, dryRun);
605628
} else {
606629
const includedInWs = ids.map((i) => this.findRobotItem(i)?.longname).filter((i) => i !== undefined) as string[];
607630
const excludedInWs =
@@ -621,7 +644,7 @@ export class TestControllerManager {
621644
suites.add(t);
622645
}
623646
}
624-
await DebugManager.runTests(workspace, Array.from(suites), includedInWs, excludedInWs, runId, options);
647+
await DebugManager.runTests(workspace, Array.from(suites), includedInWs, excludedInWs, runId, options, dryRun);
625648
}
626649
}
627650
}
@@ -671,7 +694,7 @@ export class TestControllerManager {
671694
const run = this.testRuns.get(runId);
672695

673696
if (run !== undefined) {
674-
const item = this.findTestItemById(event.attributes.longname);
697+
const item = this.findTestItemById(event.id);
675698
if (item !== undefined) {
676699
run.started(item);
677700
}
@@ -696,7 +719,7 @@ export class TestControllerManager {
696719
const run = this.testRuns.get(runId);
697720

698721
if (run !== undefined) {
699-
const item = this.findTestItemById(event.attributes.longname);
722+
const item = this.findTestItemById(event.id);
700723
if (item !== undefined) {
701724
switch (event.attributes.status) {
702725
case "PASS":

0 commit comments

Comments
 (0)