Skip to content

Commit eaacef5

Browse files
committed
Fix unit-tests for vscode 1.38.0
1 parent 246bc1a commit eaacef5

File tree

8 files changed

+184
-331
lines changed

8 files changed

+184
-331
lines changed

build/unit-tests.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ async function main() {
66
try {
77
// The folder containing the Extension Manifest package.json
88
// Passed to `--extensionDevelopmentPath`
9-
const extensionDevelopmentPath = path.resolve(__dirname, '../');
9+
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
1010

1111
// The path to the extension test runner script
1212
// Passed to --extensionTestsPath
13-
const extensionTestsPath = path.resolve(__dirname, '../test/int/index');
13+
const extensionTestsPath = path.resolve(__dirname, '../../out/test/unit/');
1414

1515
// Download VS Code, unzip it and run the integration test
16+
console.log(extensionDevelopmentPath, extensionTestsPath);
1617
await runTests({ extensionDevelopmentPath, extensionTestsPath });
1718
} catch (err) {
1819
console.error(err);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@
894894
"compile": "tsc -p ./",
895895
"watch": "tsc -watch -p ./",
896896
"clean": "rm -rf out || rmdir out /s /q",
897-
"test": "npm run clean && npm run compile && npm run verify && node ./out/build/unit-test.js",
897+
"test": "npm run clean && npm run compile && npm run verify && node ./out/build/unit-tests.js",
898898
"update-deps": "node_modules/.bin/ncu --upgrade --loglevel verbose --packageFile package.json && npm update",
899899
"coverage:upload": "codecov -f coverage/coverage-final.json",
900900
"build": "npm run clean && tslint -p tslint.json && npm run compile"

test/coverage.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import glob = require("glob");
2+
import paths = require('path');
3+
import fs = require('fs');
4+
const istanbul = require('istanbul');
5+
const remapIstanbul = require('remap-istanbul');
6+
7+
function _mkDirIfExists(dir: string): void {
8+
if (!fs.existsSync(dir)) {
9+
fs.mkdirSync(dir);
10+
}
11+
}
12+
13+
export interface ITestRunnerOptions {
14+
enabled?: boolean;
15+
relativeCoverageDir: string;
16+
relativeSourcePath: string;
17+
ignorePatterns: string[];
18+
includePid?: boolean;
19+
reports?: string[];
20+
verbose?: boolean;
21+
}
22+
23+
export class CoverageRunner {
24+
25+
private coverageVar: string = `$$cov_${new Date().getTime()}$$`;
26+
private transformer: any = undefined;
27+
private matchFn: any = undefined;
28+
private instrumenter: any = undefined;
29+
30+
constructor(private options: ITestRunnerOptions, private testsRoot: string) {
31+
if (!options.relativeSourcePath) {
32+
return;
33+
}
34+
}
35+
36+
public setupCoverage(): void {
37+
// Set up Code Coverage, hooking require so that instrumented code is returned
38+
const self = this;
39+
self.instrumenter = new istanbul.Instrumenter({ coverageVariable: self.coverageVar });
40+
const sourceRoot = paths.join(self.testsRoot, self.options.relativeSourcePath);
41+
42+
// Glob source files
43+
const srcFiles = glob.sync('**/**.js', {
44+
cwd: sourceRoot,
45+
ignore: self.options.ignorePatterns
46+
});
47+
48+
// Create a match function - taken from the run-with-cover.js in istanbul.
49+
const decache = require('decache');
50+
const fileMap: any = {};
51+
srcFiles.forEach((file) => {
52+
const fullPath = paths.join(sourceRoot, file);
53+
fileMap[fullPath] = true;
54+
55+
// On Windows, extension is loaded pre-test hooks and this mean we lose
56+
// our chance to hook the Require call. In order to instrument the code
57+
// we have to decache the JS file so on next load it gets instrumented.
58+
// This doesn't impact tests, but is a concern if we had some integration
59+
// tests that relied on VSCode accessing our module since there could be
60+
// some shared global state that we lose.
61+
decache(fullPath);
62+
});
63+
64+
self.matchFn = (file: string): boolean => fileMap[file];
65+
self.matchFn.files = Object.keys(fileMap);
66+
67+
// Hook up to the Require function so that when this is called, if any of our source files
68+
// are required, the instrumented version is pulled in instead. These instrumented versions
69+
// write to a global coverage variable with hit counts whenever they are accessed
70+
self.transformer = self.instrumenter.instrumentSync.bind(self.instrumenter);
71+
const hookOpts = { verbose: false, extensions: ['.js'] };
72+
istanbul.hook.hookRequire(self.matchFn, self.transformer, hookOpts);
73+
74+
// initialize the global variable to stop mocha from complaining about leaks
75+
global[self.coverageVar] = {};
76+
}
77+
78+
/**
79+
* Writes a coverage report.
80+
* Note that as this is called in the process exit callback, all calls must be synchronous.
81+
*
82+
* @returns {void}
83+
*
84+
* @memberOf CoverageRunner
85+
*/
86+
public reportCoverage(): void {
87+
const self = this;
88+
istanbul.hook.unhookRequire();
89+
let cov: any;
90+
if (typeof global[self.coverageVar] === 'undefined' || Object.keys(global[self.coverageVar]).length === 0) {
91+
console.error('No coverage information was collected, exit without writing coverage information');
92+
return;
93+
} else {
94+
cov = global[self.coverageVar];
95+
}
96+
97+
// TODO consider putting this under a conditional flag
98+
// Files that are not touched by code ran by the test runner is manually instrumented, to
99+
// illustrate the missing coverage.
100+
self.matchFn.files.forEach((file: any) => {
101+
if (cov[file]) {
102+
return;
103+
}
104+
self.transformer(fs.readFileSync(file, 'utf-8'), file);
105+
106+
// When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in coverState.s,
107+
// presumably to compensate for function hoisting. We need to reset this, as the function was not hoisted,
108+
// as it was never loaded.
109+
Object.keys(self.instrumenter.coverState.s).forEach((key) => {
110+
self.instrumenter.coverState.s[key] = 0;
111+
});
112+
113+
cov[file] = self.instrumenter.coverState;
114+
});
115+
116+
// TODO Allow config of reporting directory with
117+
const reportingDir = paths.join(self.testsRoot, self.options.relativeCoverageDir);
118+
const includePid = self.options.includePid;
119+
const pidExt = includePid ? ('-' + process.pid) : '';
120+
const coverageFile = paths.resolve(reportingDir, `coverage${pidExt}.json`);
121+
122+
// yes, do this again since some test runners could clean the dir initially created
123+
_mkDirIfExists(reportingDir);
124+
125+
fs.writeFileSync(coverageFile, JSON.stringify(cov), 'utf8');
126+
127+
const remappedCollector = remapIstanbul.remap(cov, {
128+
warn: (warning: any) => {
129+
// We expect some warnings as any JS file without a typescript mapping will cause this.
130+
// By default, we'll skip printing these to the console as it clutters it up
131+
if (self.options.verbose) {
132+
console.warn(warning);
133+
}
134+
}
135+
});
136+
137+
const reporter = new istanbul.Reporter(undefined, reportingDir);
138+
const reportTypes = (self.options.reports instanceof Array) ? self.options.reports : ['lcov'];
139+
reporter.addAll(reportTypes);
140+
reporter.write(remappedCollector, true, () => {
141+
console.log(`reports written to ${reportingDir}`);
142+
});
143+
}
144+
}

test/integration/index.coverage.ts

Lines changed: 0 additions & 89 deletions
This file was deleted.

test/integration/index.debug.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

test/integration/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

test/unit/extension.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ suite('openshift connector Extension', async () => {
4747
}]);
4848
const stub = sandbox.stub(Cluster, 'about');
4949
try {
50-
await vscode.commands.executeCommand('openshift.about');
50+
await vscode.commands.executeCommand('openshift.output');
5151
} catch (ignore) {
5252
} finally {
5353
stub.restore();
@@ -70,7 +70,7 @@ suite('openshift connector Extension', async () => {
7070
async function getStaticMethodsToStub(osc: string[]): Promise<string[]> {
7171
const mths: Set<string> = new Set();
7272
osc.forEach((name) => {
73-
name.replace('.palette', '');
73+
name = name.replace('.palette', '');
7474
const segs: string[] = name.split('.');
7575
let methName: string = segs[segs.length-1];
7676
methName = methName === 'delete'? 'del' : methName;

0 commit comments

Comments
 (0)