Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ language servers for [VSCode](https://code.visualstudio.com/).

After cloning the repository, run `npm install` to install dependencies and `npm run symlink` to point packages in this repository to each other.

Run `npm run test:build` to test the package command runner, or `npm test` to also run the package test suites. The package command runner continues through the selected packages and exits with the first nonzero status if any command fails. Spawn errors and signal termination are reported and cause a nonzero exit status.

## History

## 3.18.4 Protocol, 9.0.3 JSON-RPC, 10.1.2 Client and 10.1.2 Server.
Expand Down
11 changes: 10 additions & 1 deletion build/bin/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ const script = args[0] === 'run' ? args[1] : args[0];
for (const elem of folders.map(item => { return { folder: item.folder, scripts: new Set(item.scripts) }; } )) {
if (elem.scripts.has(script)) {
console.log(path.join(root, elem.folder));
child_process.spawnSync(`npm ${args.join(' ')}`, { cwd: path.join(root, elem.folder), shell: true, stdio: 'inherit' });
const result = child_process.spawnSync(`npm ${args.join(' ')}`, { cwd: path.join(root, elem.folder), shell: true, stdio: 'inherit' });
if (result.error !== undefined) {
console.error(result.error);
}
if (result.signal !== null) {
console.error(`Command terminated by signal ${result.signal}.`);
}
if (result.error !== undefined || result.signal !== null || result.status !== 0) {
process.exitCode = process.exitCode || result.status || 1;
}
}
}
121 changes: 121 additions & 0 deletions build/test/all.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

const assert = require('assert');
const child_process = require('child_process');
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const root = path.resolve(__dirname, '..', '..');
const filename = path.join(root, 'build', 'bin', 'all.js');
const source = fs.readFileSync(filename, 'utf8').replace(/^#![^\r\n]*/, '');

function run(args, results = [], env = {}) {
const calls = [];
const errors = [];
const process = { argv: ['node', filename, ...args], env, exitCode: 0 };
const execute = vm.compileFunction(source, ['require', 'process', 'console', '__dirname'], { filename });
execute((id) => {
if (id === 'child_process') {
return {
spawnSync(command, options) {
calls.push({ command, ...options });
return { status: 0, signal: null, ...results[calls.length - 1] };
}
};
}
assert.strictEqual(id, 'path');
return path;
}, process, { log() {}, error(error) { errors.push(error); } }, path.dirname(filename));
return { calls, errors, exitCode: process.exitCode };
}

suite('Package command runner', () => {
for (const status of [0, 2]) {
test(`exits the Node process with status ${status}`, () => {
const result = child_process.spawnSync(process.execPath, ['-e', `
require('child_process').spawnSync = () => ({ status: ${status}, signal: null });
process.argv = ['node', ${JSON.stringify(filename)}, 'run', 'test'];
require(${JSON.stringify(filename)});
`], { encoding: 'utf8' });
assert.ifError(result.error);
assert.strictEqual(result.signal, null);
assert.strictEqual(result.status, status, result.stderr);
});
}

test('runs tests in the existing package order', () => {
const result = run(['run', 'test']);
assert.strictEqual(result.exitCode, 0);
assert.deepStrictEqual(result.errors, []);
assert.deepStrictEqual(result.calls.map(call => path.basename(call.cwd)), [
'textDocument', 'types', 'jsonrpc', 'protocol', 'server', 'client-node-tests'
]);
for (const call of result.calls) {
assert.strictEqual(call.command, 'npm run test');
assert.strictEqual(call.shell, true);
assert.strictEqual(call.stdio, 'inherit');
assert.strictEqual(path.dirname(call.cwd), root);
}
});

for (const args of [['run', 'test'], ['run', 'lint'], ['install'], ['run', 'clean'], ['audit']]) {
test(`propagates a failed ${args.join(' ')} command and continues running packages`, () => {
const success = run(args);
const result = run(args, [{ status: 2 }]);
assert.strictEqual(result.exitCode, 2);
assert.deepStrictEqual(result.calls, success.calls);
});
}

test('preserves the first failure when later commands fail or succeed', () => {
const result = run(['run', 'test'], [{ status: 2 }, { status: 3 }]);
assert.strictEqual(result.exitCode, 2);
assert.strictEqual(result.calls.length, 6);
});

test('propagates a failure in the last package', () => {
const result = run(['run', 'test'], [{}, {}, {}, {}, {}, { status: 7 }]);
assert.strictEqual(result.exitCode, 7);
});

test('reports spawn errors and exits unsuccessfully', () => {
const error = new Error('Unable to spawn npm');
const result = run(['run', 'test'], [{ status: null, error }]);
assert.strictEqual(result.exitCode, 1);
assert.deepStrictEqual(result.errors, [error]);
assert.strictEqual(result.calls.length, 6);
});

test('reports signal termination and exits unsuccessfully', () => {
const result = run(['run', 'test'], [{ status: null, signal: 'SIGTERM' }]);
assert.strictEqual(result.exitCode, 1);
assert.deepStrictEqual(result.errors, ['Command terminated by signal SIGTERM.']);
assert.strictEqual(result.calls.length, 6);
});

test('does not treat a missing exit status as success', () => {
assert.strictEqual(run(['run', 'test'], [{ status: null }]).exitCode, 1);
});

test('preserves additional command arguments', () => {
const result = run(['audit', 'fix']);
assert.strictEqual(result.exitCode, 0);
assert.strictEqual(result.calls.length, 7);
assert.ok(result.calls.every(call => call.command === 'npm audit fix'));
});

test('skips package installation for root-only installs', () => {
const result = run(['install'], [], { npm_config_root_only: 'true' });
assert.deepStrictEqual(result.calls, []);
assert.strictEqual(result.exitCode, 0);
});

test('root-only installation setting does not skip tests', () => {
assert.strictEqual(run(['run', 'test'], [], { npm_config_root_only: 'true' }).calls.length, 6);
});
});
32 changes: 24 additions & 8 deletions client-node-tests/src/converter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ declare module 'vscode' {
}

import { strictEqual, deepEqual, ok, deepStrictEqual } from 'assert';
import * as sinon from 'sinon';

import * as proto from 'vscode-languageclient';
import * as codeConverter from 'vscode-languageclient/$test/common/codeConverter';
Expand Down Expand Up @@ -89,6 +90,8 @@ function assertDiagnosticCode(value: string | number | DiagnosticCode | undefine

suite('Async Array', () => {

let clock: sinon.SinonFakeTimers;

suiteSetup(() => {
async.setTestMode();
});
Expand All @@ -97,14 +100,25 @@ suite('Async Array', () => {
async.clearTestMode();
});

setup(() => {
clock = sinon.useFakeTimers({ toFake: ['Date'] });
});

teardown(() => {
clock.restore();
});

test('map', async() => {
const ranges: proto.Range[] = new Array(20000);
const ranges: proto.Range[] = new Array(10);
for (let i = 0; i < ranges.length; i++) {
ranges[i] = proto.Range.create(i, i, i, i);
}
let yielded = 0;
const converted = await async.map(ranges, p2c.asRange, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }});
ok(yielded > 0);
const converted = await async.map(ranges, (item) => {
clock.tick(2);
return p2c.asRange(item);
}, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }});
strictEqual(yielded, ranges.length);
strictEqual(converted.length, ranges.length);
for (let i = 0; i < converted.length; i++) {
ok(converted[i] instanceof vscode.Range);
Expand All @@ -113,19 +127,20 @@ suite('Async Array', () => {
}).timeout(5000);

test('map async', async() => {
const ranges: proto.Range[] = new Array(5000);
const ranges: proto.Range[] = new Array(10);
for (let i = 0; i < ranges.length; i++) {
ranges[i] = proto.Range.create(i, i, i, i);
}
let yielded = 0;
const converted = await async.mapAsync(ranges, (item): Promise<vscode.Range> => {
return new Promise((resolve) => {
proto.RAL().timer.setImmediate(() => {
clock.tick(2);
resolve(p2c.asRange(item));
});
});
}, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }});
ok(yielded > 0);
strictEqual(yielded, ranges.length);
strictEqual(converted.length, ranges.length);
for (let i = 0; i < converted.length; i++) {
ok(converted[i] instanceof vscode.Range);
Expand All @@ -134,18 +149,19 @@ suite('Async Array', () => {
}).timeout(5000);

test('forEach', async() => {
const ranges: proto.Range[] = new Array(30000);
const ranges: proto.Range[] = new Array(10);
for (let i = 0; i < ranges.length; i++) {
ranges[i] = proto.Range.create(i + 1, 0, i + 2, 1);
}
let sum: number = 0;
let yielded = 0;
await async.forEach(ranges, (item) => {
clock.tick(2);
const codeRange = p2c.asRange(item);
sum += codeRange.start.line;
}, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }});
ok(yielded > 0);
strictEqual(sum, 450015000);
strictEqual(yielded, ranges.length);
strictEqual(sum, 55);
}).timeout(5000);
});

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
},
"scripts": {
"postinstall": "node ./build/bin/all.js install && cd testbed && npm install && cd .. && npm run symlink:testbed && npx playwright install",
"test": "node ./build/bin/all.js run test",
"test": "npm run test:build && node ./build/bin/all.js run test",
"test:build": "mocha --ui tdd \"build/test/**/*.test.js\"",
"clean": "node ./build/bin/all.js run clean",
"symlink": "node ./build/bin/symlink.js",
"symlink:tests": "node ./build/bin/symlink-tests.js",
Expand Down
Loading