Skip to content

Commit 07c24d7

Browse files
committed
Improvements
1 parent a3105e4 commit 07c24d7

File tree

8 files changed

+320
-248
lines changed

8 files changed

+320
-248
lines changed

src/commonRunTestsHandler.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
1414

1515
// For each authority (i.e. server:namespace) accumulate a map of the class-level Test nodes in the tree.
1616
// We don't yet support running only some TestXXX methods in a testclass
17-
const mapAuthorities = new Map<string, Map<string, vscode.TestItem>>();
17+
const mapAuthorities = new Map<string, Map<string, OurTestItem>>();
1818
const runIndices: number[] =[];
1919
const queue: OurTestItem[] = [];
2020
const coverageRequest = request.profile?.kind === vscode.TestRunProfileKind.Coverage;
@@ -56,8 +56,8 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
5656
}
5757

5858
// If a leaf item (a TestXXX method in a class) note its .cls file for copying.
59-
// Every leaf must have a uri.
60-
if (test.children.size === 0 && test.uri && test.parent) {
59+
// Every leaf should have a uri.
60+
if (test.children.size === 0 && test.uri) {
6161
let authority = test.uri.authority;
6262
let key = test.uri.path;
6363
if (test.uri.scheme === "file") {
@@ -70,9 +70,13 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
7070
}
7171
}
7272

73-
const mapTestClasses = mapAuthorities.get(authority) || new Map<string, vscode.TestItem>();
74-
mapTestClasses.set(key, test.parent);
75-
mapAuthorities.set(authority, mapTestClasses);
73+
const mapTestClasses = mapAuthorities.get(authority) || new Map<string, OurTestItem>();
74+
if (!mapTestClasses.has(key) && test.parent) {
75+
// When leaf is a test its parent has a uri and is the class
76+
// Otherwise the leaf is a class with no tests
77+
mapTestClasses.set(key, test.parent.uri ? test.parent : test);
78+
mapAuthorities.set(authority, mapTestClasses);
79+
}
7680
}
7781

7882
// Queue any children
@@ -109,8 +113,16 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
109113
);
110114
let authority = mapInstance[0];
111115
const mapTestClasses = mapInstance[1];
116+
117+
// enqueue everything up front so user sees immediately which tests will run
118+
mapTestClasses.forEach((test) => {
119+
test.children.forEach((methodTest) => {
120+
run.enqueued(methodTest);
121+
});
122+
});
123+
112124
const firstClassTestItem = Array.from(mapTestClasses.values())[0];
113-
const oneUri = firstClassTestItem.uri;
125+
const oneUri = firstClassTestItem.ourUri;
114126

115127
// This will always be true since every test added to the map above required a uri
116128
if (oneUri) {
@@ -166,11 +178,16 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
166178
const key = mapInstance[0];
167179
const pathParts = key.split('/');
168180
pathParts.pop();
169-
const sourceBaseUri = mapInstance[1].uri?.with({ path: mapInstance[1].uri.path.split('/').slice(0, -pathParts.length).join('/') });
181+
const sourceBaseUri = mapInstance[1].ourUri?.with({ path: mapInstance[1].ourUri.path.split('/').slice(0, -pathParts.length).join('/') });
170182
if (!sourceBaseUri) {
171183
console.log(`No sourceBaseUri for key=${key}`);
172184
continue;
173185
}
186+
// isfs folders can't supply coverage.list files, so don't bother looking.
187+
// Instead the file has to be put in the /namespace/UnitTestRoot/ folder of the /_vscode webapp of the %SYS namespace.
188+
if (['isfs', 'isfs-readonly'].includes(sourceBaseUri.scheme)) {
189+
continue;
190+
}
174191
while (pathParts.length > 1) {
175192
const currentPath = pathParts.join('/');
176193
// Check for coverage.list file here
@@ -217,6 +234,7 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
217234
await vscode.workspace.fs.copy(uri, directoryUri.with({ path: directoryUri.path.concat(clsFile) }));
218235
} catch (error) {
219236
console.log(error);
237+
run.errored(classTest, new vscode.TestMessage(error instanceof Error ? error.message : String(error)));
220238
continue;
221239
}
222240

@@ -241,8 +259,8 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
241259
let testSpec = username;
242260
if (request.include?.length === 1) {
243261
const idParts = request.include[0].id.split(":");
244-
if (idParts.length === 4) {
245-
testSpec = `${username}:${idParts[2]}:${idParts[3]}`;
262+
if (idParts.length === 5) {
263+
testSpec = `${username}:${idParts[3]}:${idParts[4]}`;
246264
}
247265
}
248266

@@ -264,7 +282,7 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
264282

265283
// Extra properties needed by our DebugAdapterTracker
266284
testingRunIndex: runIndex,
267-
testingIdBase: firstClassTestItem.id.split(":", 2).join(":")
285+
testingIdBase: firstClassTestItem.id.split(":", 3).join(":")
268286
};
269287
const sessionOptions: vscode.DebugSessionOptions = {
270288
noDebug: !isDebug,

src/coverage.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,21 @@ export async function getFileCoverageResults(folderUri: vscode.Uri, namespace: s
5050
if (pathPrefix && !pathPrefix.startsWith('/')) {
5151
pathPrefix = `/${pathPrefix}`;
5252
}
53-
if (exportSettings.atelier) {
53+
if (exportSettings.addCategory) {
54+
// TODO handle rare(?) Object-format addCategory setting just like the ObjectScript extension implements in src/commands/export.ts
5455
pathPrefix += '/' + fileType;
5556
}
5657
}
58+
59+
// Respect exportSettings.map which the IPM project uses to export %IPM.Foo.cls into IPM/Foo.cls
60+
if (exportSettings.map) {
61+
for (const pattern of Object.keys(exportSettings.map)) {
62+
if (new RegExp(`^${pattern}$`).test(element.Name)) {
63+
element.Name = element.Name.replace(new RegExp(`^${pattern}$`), exportSettings.map[pattern]);
64+
break;
65+
}
66+
}
67+
}
5768
const fileUri = folderUri.with({ path: folderUri.path.concat(pathPrefix, `/${element.Name.replace(/\./g, '/')}.${fileType}`) });
5869
fileCoverage = new OurFileCoverage(
5970
coverageIndex,
@@ -66,7 +77,7 @@ export async function getFileCoverageResults(folderUri: vscode.Uri, namespace: s
6677
}
6778
const testPath: string = element.TestPath || 'all tests';
6879
if (testPath !== 'all tests') {
69-
console.log(`Find TestItem matching test path ${testPath}`);
80+
//console.log(`Find TestItem matching test path ${testPath}`);
7081
const className = testPath.split(':')[1];
7182
const testItem = workspaceFolderTestClasses[vscode.workspace.getWorkspaceFolder(folderUri)?.index || 0].get(className);
7283
if (testItem) {

src/debugTracker.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class DebugTracker implements vscode.DebugAdapterTracker {
1717
private methodTestMap: Map<string, vscode.TestItem>;
1818
private methodTest?: vscode.TestItem;
1919
private failureMessages: vscode.TestMessage[] = [];
20+
private skippedMessages: vscode.TestMessage[] = [];
2021

2122
constructor(session: vscode.DebugSession) {
2223
this.session = session;
@@ -62,7 +63,7 @@ export class DebugTracker implements vscode.DebugAdapterTracker {
6263
if (coverageMatch && this.run.debugSession) {
6364
const coverageIndex = Number(coverageMatch[1]);
6465
this.run.debugSession.configuration.coverageIndex = coverageIndex;
65-
console.log(`Coverage index set to ${coverageIndex}`);
66+
//console.log(`Coverage index set to ${coverageIndex}`);
6667
}
6768

6869
if (this.className === undefined) {
@@ -99,6 +100,11 @@ export class DebugTracker implements vscode.DebugAdapterTracker {
99100
this.run.passed(this.methodTest, this.testDuration)
100101
break;
101102

103+
case 'skipped':
104+
// Pending https://github.com/microsoft/vscode/issues/133198 we can't do anything with this.skippedMessages
105+
this.run.skipped(this.methodTest);
106+
break;
107+
102108
case 'failed':
103109
this.run.failed(this.methodTest, this.failureMessages.length > 0 ? this.failureMessages : { message: 'Failed with no messages' }, this.testDuration);
104110
break;
@@ -111,6 +117,7 @@ export class DebugTracker implements vscode.DebugAdapterTracker {
111117
this.testDuration = undefined;
112118
this.methodTest = undefined;
113119
this.failureMessages = [];
120+
this.skippedMessages = [];
114121
return;
115122
}
116123
}
@@ -129,13 +136,21 @@ export class DebugTracker implements vscode.DebugAdapterTracker {
129136
//console.log(`Class ${this.className}, Test-method ${this.testMethodName}, macroName ${macroName}, outcome 'failed', message=${message}`);
130137
this.failureMessages.push({ message: message });
131138
} else {
132-
const logMessageMatch = line.match(/^ LogMessage:(.*)$/);
133-
if (logMessageMatch) {
134-
const message = logMessageMatch[1];
135-
//console.log(`Class ${this.className}, Test-method ${this.testMethodName}, macroName LogMessage, message=${message}`);
136-
const duration = message.match(/^Duration of execution: (\d*\.\d+) sec.$/);
137-
if (duration) {
138-
this.testDuration = + duration[1] * 1000;
139+
const assertSkippedMatch = line.match(/^ (Test\w+):(.*) \(skipped\)$/);
140+
if (assertSkippedMatch) {
141+
//const macroName = assertSkippedMatch[1];
142+
const message = assertSkippedMatch[2];
143+
//console.log(`Class ${this.className}, Test-method ${this.testMethodName}, macroName ${macroName}, outcome 'skipped', message=${message}`);
144+
this.skippedMessages.push({ message: message });
145+
} else {
146+
const logMessageMatch = line.match(/^ LogMessage:(.*)$/);
147+
if (logMessageMatch) {
148+
const message = logMessageMatch[1];
149+
//console.log(`Class ${this.className}, Test-method ${this.testMethodName}, macroName LogMessage, message=${message}`);
150+
const duration = message.match(/^Duration of execution: (\d*\.\d+) sec.$/);
151+
if (duration) {
152+
this.testDuration = + duration[1] * 1000;
153+
}
139154
}
140155
}
141156
}

src/extension.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as vscode from "vscode";
44
import * as serverManager from "@intersystems-community/intersystems-servermanager";
55
import { setupHistoryExplorerController } from "./historyExplorer";
66
import { setupServerTestsController } from "./serverTests";
7-
import { setupLocalTestsController } from "./localTests";
7+
import { replaceLocalRootItems, setupLocalTestsController } from "./localTests";
88
import { DebugTrackerFactory } from "./debugTrackerFactory";
99

1010
export const extensionId = "intersystems-community.testingmanager";
@@ -19,13 +19,14 @@ export interface OurTestRun extends vscode.TestRun {
1919
}
2020

2121
export interface OurTestItem extends vscode.TestItem {
22+
ourUri?: vscode.Uri;
2223
supportsCoverage?: boolean
2324
}
2425

2526
export const allTestRuns: (OurTestRun | undefined)[] = [];
2627

2728
// Array indexed by workspaceFolder index, each element holding a map from classname ('P1.P2.C') to TestItem
28-
export const workspaceFolderTestClasses: Map<string, OurTestItem>[] = [];
29+
export let workspaceFolderTestClasses: Map<string, OurTestItem>[] = [];
2930

3031
async function getServerManagerAPI(): Promise<serverManager.ServerManagerAPI | undefined> {
3132
const targetExtension = vscode.extensions.getExtension("intersystems-community.servermanager");
@@ -66,12 +67,20 @@ export async function activate(context: vscode.ExtensionContext) {
6667
// TODO notify user if either of these returned undefined (extensionDependencies setting should prevent that, but better to be safe)
6768

6869

69-
// TODO handle changes to workspace structure, e.g. workspaceFolder added or removed
70-
vscode.workspace.workspaceFolders?.forEach((folder, index) => {
71-
// Initialize the map for this workspace folder's test class items
72-
const newLength = workspaceFolderTestClasses.push(new Map<string, OurTestItem>());
73-
console.log(`Initialized workspaceFolderTestClasses[${index}] with length ${newLength}`);
74-
});
70+
const initWorkspaceFolderTestClasses = () => {
71+
workspaceFolderTestClasses = [];
72+
vscode.workspace.workspaceFolders?.forEach(() => {
73+
// Initialize the map for this workspace folder's test class items
74+
workspaceFolderTestClasses.push(new Map<string, OurTestItem>());
75+
});
76+
}
77+
initWorkspaceFolderTestClasses();
78+
context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async (e) => {
79+
initWorkspaceFolderTestClasses();
80+
await replaceLocalRootItems(localTestController);
81+
}));
82+
83+
7584

7685
// Other parts of this extension will use the test controllers we create here
7786
localTestController = vscode.tests.createTestController(`${extensionId}-Local`, '$(folder-library) Local Tests');

0 commit comments

Comments
 (0)