Skip to content

Commit

Permalink
add test tree
Browse files Browse the repository at this point in the history
Signed-off-by: issacto <[email protected]>
  • Loading branch information
issacto committed Aug 13, 2023
1 parent 847d2c4 commit d3bd505
Show file tree
Hide file tree
Showing 10 changed files with 699 additions and 15 deletions.
3 changes: 3 additions & 0 deletions vs code extension/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"dependencies": {
"vscode-languageclient": "^7.0.0"
},
"activationEvents": [
"workspaceContains:*.cut"
],
"devDependencies": {
"@types/glob": "^7.1.4",
"@types/node": "^15.12.2",
Expand Down
205 changes: 205 additions & 0 deletions vs code extension/client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { startCutLanguageClientServer, stopCutLanguageClientServer } from './ser
import { ResultWebView } from './services/ResultWebView';
import { handleCobolCheckOut } from './Helpers/ExtensionHelper';
import path = require('path');
import { getContentFromFilesystem, MarkdownTestData, TestCase, testData, TestFile } from "./services/testTree";


let externalVsCodeInstallationDir = vscode.extensions.getExtension("openmainframeproject.cobol-check-extension").extensionPath;
let configPath = appendPath(externalVsCodeInstallationDir, 'Cobol-check/config.properties');
Expand All @@ -29,6 +31,10 @@ export function activate(context: ExtensionContext) {
startCutLanguageClientServer(context);

const provider = new ResultWebView(context.extensionUri);
const ctrl = vscode.tests.createTestController('CobolCheckController', 'Cobol Check');
context.subscriptions.push(ctrl);

const fileChangedEmitter = new vscode.EventEmitter<vscode.Uri>();

context.subscriptions.push(
vscode.window.registerWebviewViewProvider(ResultWebView.viewType, provider));
Expand Down Expand Up @@ -79,10 +85,209 @@ export function activate(context: ExtensionContext) {
context.subscriptions.push(runCobolCheck_Cmd);
context.subscriptions.push(setConfiguration_Cmd);
context.subscriptions.push(resetConfigurations_Cmd);

const runHandler = (request: vscode.TestRunRequest2, cancellation: vscode.CancellationToken) => {
if (!request.continuous) {
return startTestRun(request);
}

const l = fileChangedEmitter.event(uri => startTestRun(
new vscode.TestRunRequest2(
[getOrCreateFile(ctrl, uri).file],
undefined,
request.profile,
true
),
));
cancellation.onCancellationRequested(() => l.dispose());
};

const startTestRun = (request: vscode.TestRunRequest) => {
const queue: { test: vscode.TestItem; data: TestCase }[] = [];
const run = ctrl.createTestRun(request);
// map of file uris to statements on each line:
const coveredLines = new Map</* file uri */ string, (vscode.StatementCoverage | undefined)[]>();

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 TestCase) {
run.enqueued(test);
queue.push({ test, data });
} else {
if (data instanceof TestFile && !data.didResolve) {
await data.updateFromDisk(ctrl, test);
}

await discoverTests(gatherTestItems(test.children));
}

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 { test, data } of queue) {
run.appendOutput(`Running ${test.id}\r\n`);
if (run.token.isCancellationRequested) {
run.skipped(test);
} else {
run.started(test);
await data.run(test, run);
}

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

run.appendOutput(`Completed ${test.id}\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);
};

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

ctrl.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, runHandler, true, undefined, true);

ctrl.resolveHandler = async item => {
if (!item) {
context.subscriptions.push(...startWatchingWorkspace(ctrl, fileChangedEmitter));
return;
}

const data = testData.get(item);
if (data instanceof TestFile) {
await data.updateFromDisk(ctrl, item);
}
};

function updateNodeForDocument(e: vscode.TextDocument) {
if (e.uri.scheme !== 'file') {
return;
}

if (!e.uri.path.endsWith('.cut')) {
return;
}

const { file, data } = getOrCreateFile(ctrl, e.uri);
data.updateFromContents(ctrl, e.getText(), file);
}

for (const document of vscode.workspace.textDocuments) {
updateNodeForDocument(document);
}

context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(updateNodeForDocument),
vscode.workspace.onDidChangeTextDocument(e => updateNodeForDocument(e.document)),
);
}

export function deactivate() {
stopCutLanguageClientServer();
}

// Functions for activating tests
function getOrCreateFile(controller: vscode.TestController, uri: vscode.Uri) {
const existing = controller.items.get(uri.toString());
if (existing) {
return { file: existing, data: testData.get(existing) as TestFile };
}

const file = controller.createTestItem(uri.toString(), uri.path.split('/').pop()!, uri);
controller.items.add(file);

const data = new TestFile();
testData.set(file, data);

file.canResolveChildren = true;
return { file, data };
}

function gatherTestItems(collection: vscode.TestItemCollection) {
const items: vscode.TestItem[] = [];
collection.forEach(item => items.push(item));
return items;
}

function getWorkspaceTestPatterns() {
if (!vscode.workspace.workspaceFolders) {
return [];
}

return vscode.workspace.workspaceFolders.map(workspaceFolder => ({
workspaceFolder,
pattern: new vscode.RelativePattern(workspaceFolder, '**/*.cut'),
}));
}

async function findInitialFiles(controller: vscode.TestController, pattern: vscode.GlobPattern) {
for (const file of await vscode.workspace.findFiles(pattern)) {
getOrCreateFile(controller, file);
}
}

function startWatchingWorkspace(controller: vscode.TestController, fileChangedEmitter: vscode.EventEmitter<vscode.Uri> ) {
return getWorkspaceTestPatterns().map(({ workspaceFolder, pattern }) => {
const watcher = vscode.workspace.createFileSystemWatcher(pattern);

watcher.onDidCreate(uri => {
getOrCreateFile(controller, uri);
fileChangedEmitter.fire(uri);
});
watcher.onDidChange(async uri => {
const { file, data } = getOrCreateFile(controller, uri);
if (data.didResolve) {
await data.updateFromDisk(controller, file);
}
fileChangedEmitter.fire(uri);
});
watcher.onDidDelete(uri => controller.items.delete(uri.toString()));

findInitialFiles(controller, pattern);

return watcher;
});
}

33 changes: 33 additions & 0 deletions vs code extension/client/src/services/CobolCheckInputParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as vscode from 'vscode';

const testCaseRegex = /^\s*(TESTCASE)\s*(.*)/i;
// const testRegex = /^\s*(Expect)\s*(.*)\s*(not?)\s(>|<|to\s*be)\s*(.*)\s*/i;
const testSuiteRegex = /^\s*(TESTSUITE)\s*(.*)/;

export const parseMarkdown = (text: string, events: {
onTest(range: vscode.Range, name: string): void;
onHeading(range: vscode.Range, name: string, depth: number): void;
}) => {
const lines = text.split('\n');

for (let lineNo = 0; lineNo < lines.length; lineNo++) {
const line = lines[lineNo];

const testCase = testCaseRegex.exec(line);
if (testCase) {
var [,pounds, name] = testCase;
name = name.replace(/['"]+/g, '');
const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, testCase[0].length));
events.onTest(range, name);
continue;
}


const testSuite = testSuiteRegex.exec(line);
if (testSuite) {
var [,pounds, name] = testSuite;
const range = new vscode.Range(new vscode.Position(lineNo, line.indexOf(pounds)), new vscode.Position(lineNo, line.indexOf(name) + name.length));
events.onHeading(range, name, 1);
}
}
};
Loading

0 comments on commit d3bd505

Please sign in to comment.