Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #7 from MarkusAmshove/debug-profile
Browse files Browse the repository at this point in the history
Add run profile in debug mode if supported
  • Loading branch information
MarkusAmshove committed Nov 3, 2023
2 parents 89a5fe3 + ed0465e commit 450c3a2
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 103 deletions.
237 changes: 136 additions & 101 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ let natparmItem: vscode.StatusBarItem;
export async function activate(context: vscode.ExtensionContext) {
const ctrl = vscode.tests.createTestController('natunitTestController', 'NatUnit');
context.subscriptions.push(ctrl);
context.subscriptions.push(vscode.languages.registerCodeLensProvider('natural', new NatUnitCodeLensProvider()));

const hasDebugCapabilities = await canDebugTests();

context.subscriptions.push(vscode.languages.registerCodeLensProvider('natural', new NatUnitCodeLensProvider(hasDebugCapabilities)));

context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => {
if(e.affectsConfiguration("natunit")) {
Expand All @@ -24,112 +27,16 @@ export async function activate(context: vscode.ExtensionContext) {
reloadConfiguration();
setupNatparmInfo();

const runHandler = async (request: vscode.TestRunRequest, cancellation: vscode.CancellationToken) => {
const queue: NatUnitTestCase[] = [];

// Convert single test requests to a testcase request, because NatUnit can only run all tests in a testcase
const newIncludes: vscode.TestItem[] = [];
for(let include of request.include || []) {
const testCase = testToTestCase.get(include);
if(!testCase?.didResolve) {
await testCase?.updateFromDisk(ctrl, include);
}
testCase?.testsInCase.forEach(t => newIncludes.push(t));
}

const testCaseRequest : vscode.TestRunRequest = {
include: Array.from(new Set(newIncludes)),
exclude: request.exclude,
profile: request.profile
};

const natparm = natunitConfig.currentNatparm;
const run = ctrl.createTestRun(testCaseRequest, natparm, false);

const discoverTests = async (tests: Iterable<vscode.TestItem>) => {
for (const test of tests) {
if (request.exclude?.includes(test)) {
continue;
}

const data = testData.get(test);
if (data instanceof NatUnitTest) {
const testCase = testData.get(test.parent!) as NatUnitTestCase;
testCase.testsInCase.forEach(t => run.enqueued(t));
queue.push( testCase );
} else {
if (data instanceof NatUnitTestCase && !data.didResolve) {
await data.updateFromDisk(ctrl, test);
}

if (data instanceof NatUnitTestCase) {
data.testsInCase.forEach(t => run.enqueued(t));
queue.push(data);
}
}

// if (test.uri && !coveredLines.has(test.uri.toString())) {
// try {
// const lines = (await getContentFromFilesystem(test.uri)).split('\n');
// coveredLines.set(
// test.uri.toString(),
// lines.map((lineText, lineNo) =>
// lineText.trim().length ? new vscode.StatementCoverage(0, new vscode.Position(lineNo, 0)) : undefined
// )
// );
// } catch {
// // ignored
// }
// }
}
};

const runTestQueue = async () => {
for (const data of queue) {
run.appendOutput(`Running ${data.name}\r\n`);
const testsInCase = data.testsInCase;
if (cancellation.isCancellationRequested) {
testsInCase.forEach(t => run.skipped(t));
} else {
testsInCase.forEach(t => run.started(t));
await data.run(run, natparm);
}

// const lineNo = test.range!.start.line;
// const fileCoverage = coveredLines.get(test.uri!.toString());
// if (fileCoverage) {
// fileCoverage[lineNo]!.executionCount++;
// }

run.appendOutput(`Completed ${data.name}\r\n`);
}

run.end();
};

// run.coverageProvider = {
// provideFileCoverage() {
// const coverage: vscode.FileCoverage[] = [];
// for (const [uri, statements] of coveredLines) {
// coverage.push(
// vscode.FileCoverage.fromDetails(
// vscode.Uri.parse(uri),
// statements.filter((s): s is vscode.StatementCoverage => !!s)
// )
// );
// }

// return coverage;
// },
// };
discoverTests(request.include ?? gatherTestItems(ctrl.items)).then(runTestQueue);
};
const runHandler = createRunHandler(ctrl, false);

ctrl.refreshHandler = async () => {
await Promise.all(getWorkspaceTestPatterns().map(({ pattern }) => findInitialFiles(ctrl, pattern)));
};

ctrl.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, runHandler, true);
if (hasDebugCapabilities) {
ctrl.createRunProfile('Debug Tests', vscode.TestRunProfileKind.Debug, createRunHandler(ctrl, true), false);
}

ctrl.resolveHandler = async item => {
if (!item) {
Expand Down Expand Up @@ -260,3 +167,131 @@ async function changeNatparm() {
}
}

function createRunHandler(ctrl: vscode.TestController, isDebug: boolean) {
return async (request: vscode.TestRunRequest, cancellation: vscode.CancellationToken) => {
const queue: NatUnitTestCase[] = [];

// Convert single test requests to a testcase request, because NatUnit can only run all tests in a testcase
const newIncludes: vscode.TestItem[] = [];
for(let include of request.include || []) {
const testCase = testToTestCase.get(include);
if(!testCase?.didResolve) {
await testCase?.updateFromDisk(ctrl, include);
}
testCase?.testsInCase.forEach(t => newIncludes.push(t));
}

const testCaseRequest : vscode.TestRunRequest = {
include: Array.from(new Set(newIncludes)),
exclude: request.exclude,
profile: request.profile
};

const natparm = natunitConfig.currentNatparm;
const run = ctrl.createTestRun(testCaseRequest, natparm, false);

const discoverTests = async (tests: Iterable<vscode.TestItem>) => {
for (const test of tests) {
if (request.exclude?.includes(test)) {
continue;
}

const data = testData.get(test);
if (data instanceof NatUnitTest) {
const testCase = testData.get(test.parent!) as NatUnitTestCase;
testCase.testsInCase.forEach(t => run.enqueued(t));
queue.push( testCase );
} else {
if (data instanceof NatUnitTestCase && !data.didResolve) {
await data.updateFromDisk(ctrl, test);
}

if (data instanceof NatUnitTestCase) {
data.testsInCase.forEach(t => run.enqueued(t));
queue.push(data);
}
}

// if (test.uri && !coveredLines.has(test.uri.toString())) {
// try {
// const lines = (await getContentFromFilesystem(test.uri)).split('\n');
// coveredLines.set(
// test.uri.toString(),
// lines.map((lineText, lineNo) =>
// lineText.trim().length ? new vscode.StatementCoverage(0, new vscode.Position(lineNo, 0)) : undefined
// )
// );
// } catch {
// // ignored
// }
// }
}
};

const runTestQueue = async () => {
for (const data of queue) {
run.appendOutput(`Running ${data.name}\r\n`);
const testsInCase = data.testsInCase;
if (cancellation.isCancellationRequested) {
testsInCase.forEach(t => run.skipped(t));
} else {
testsInCase.forEach(t => run.started(t));
if (isDebug) {
const folders = vscode.workspace.workspaceFolders;
await vscode.debug.startDebugging(folders ? folders[0] : undefined, debugConfig(data.path));
} else {
await data.run(run, natparm);
}
}

// const lineNo = test.range!.start.line;
// const fileCoverage = coveredLines.get(test.uri!.toString());
// if (fileCoverage) {
// fileCoverage[lineNo]!.executionCount++;
// }

run.appendOutput(`Completed ${data.name}\r\n`);
}

run.end();
};

// run.coverageProvider = {
// provideFileCoverage() {
// const coverage: vscode.FileCoverage[] = [];
// for (const [uri, statements] of coveredLines) {
// coverage.push(
// vscode.FileCoverage.fromDetails(
// vscode.Uri.parse(uri),
// statements.filter((s): s is vscode.StatementCoverage => !!s)
// )
// );
// }

// return coverage;
// },
// };
discoverTests(request.include ?? gatherTestItems(ctrl.items)).then(runTestQueue);
};
}

function debugConfig(testcase: string) : vscode.DebugConfiguration {
return {
name: "Debug Testcase",
request: "launch",
type: "natural",
programm: testcase,
workspace: "${workspaceFolder}"
}

Check warning on line 285 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Missing semicolon
}

async function canDebugTests() : Promise<boolean> {
const bob = vscode.extensions.getExtension('alteoldenburger.vscode-bob');
if (!bob) {
return false;
}
if (!bob.isActive) {
await bob.activate();
}
return true;
}
18 changes: 16 additions & 2 deletions src/natunit/codelenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class NatUnitCodeLensProvider implements vscode.CodeLensProvider {

private codeLenses : vscode.CodeLens[] = [];

constructor(private hasDebugCapabilities: boolean) {}

provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
this.codeLenses = [];

Expand All @@ -30,12 +32,24 @@ export class NatUnitCodeLensProvider implements vscode.CodeLensProvider {
}

private createCodeLensForRange(range: vscode.Range) : vscode.CodeLens[] {
return [
const lenses = [
new vscode.CodeLens(range, {
title: "$(testing-run-icon) Run",
tooltip: "Run the current test case",
command: "testing.runCurrentFile"
}),
]
];

if (this.hasDebugCapabilities) {
lenses.push(
new vscode.CodeLens(range, {
title: "$(testing-debug-icon) Debug",
tooltip: "Debug the current test case",
command: "testing.debugCurrentFile"
}),
);
}

return lenses;
}
};
2 changes: 2 additions & 0 deletions src/natunit/testTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ export class NatUnitTestCase {
public didResolve = false

Check warning on line 30 in src/natunit/testTree.ts

View workflow job for this annotation

GitHub Actions / build

Missing semicolon
private tests: vscode.TestItem[] = [];
public name: string = "";
public path: string = "";

public get testsInCase() {
return this.tests;
}

public async updateFromDisk(controller: vscode.TestController, item: vscode.TestItem) {
try {
this.path = item.uri!.path;
this.name = item.uri!.path.split('/').pop()!.replace(".NSN", "");
const content = await getContentFromFilesystem(item.uri!);
item.error = undefined;
Expand Down

0 comments on commit 450c3a2

Please sign in to comment.