Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import { TestServiceClient } from './testService';
import { getPathFromAmdModule } from 'vs/base/common/amd';
function createClient(): Client {
return new Client(getPathFromAmdModule(require, 'bootstrap-fork'), {
serverName: 'TestServer',
env: { AMD_ENTRYPOINT: 'vs/base/parts/ipc/test/node/testApp', verbose: true }
});
}
suite('IPC, Child Process', () => {
test('createChannel', () => {
const client = createClient();
const channel = client.getChannel('test');
const service = new TestServiceClient(channel);
const result = service.pong('ping').then(r => {
assert.equal(r.incoming, 'ping');
assert.equal(r.outgoing, 'pong');
});
return result.finally(() => client.dispose());
});
test('events', () => {
const client = createClient();
const channel = client.getChannel('test');
const service = new TestServiceClient(channel);
const event = new Promise((c, e) => {
service.onMarco(({ answer }) => {
try {
assert.equal(answer, 'polo');
c(undefined);
} catch (err) {
e(err);
}
});
});
const request = service.marco();
const result = Promise.all([request, event]);
return result.finally(() => client.dispose());
});
test('event dispose', () => {
const client = createClient();
const channel = client.getChannel('test');
const service = new TestServiceClient(channel);
let count = 0;
const disposable = service.onMarco(() => count++);
const result = service.marco().then(async answer => {
assert.equal(answer, 'polo');
assert.equal(count, 1);
const answer_1 = await service.marco();
assert.equal(answer_1, 'polo');
assert.equal(count, 2);
disposable.dispose();
const answer_2 = await service.marco();
assert.equal(answer_2, 'polo');
assert.equal(count, 2);
});
return result.finally(() => client.dispose());
});
});

View File

@@ -0,0 +1,258 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { createServer, Socket } from 'net';
import { EventEmitter } from 'events';
import { Protocol, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
import { createRandomIPCHandle, createStaticIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { VSBuffer } from 'vs/base/common/buffer';
import { tmpdir } from 'os';
import product from 'vs/platform/product/common/product';
class MessageStream {
private _currentComplete: ((data: VSBuffer) => void) | null;
private _messages: VSBuffer[];
constructor(x: Protocol | PersistentProtocol) {
this._currentComplete = null;
this._messages = [];
x.onMessage(data => {
this._messages.push(data);
this._trigger();
});
}
private _trigger(): void {
if (!this._currentComplete) {
return;
}
if (this._messages.length === 0) {
return;
}
const complete = this._currentComplete;
const msg = this._messages.shift()!;
this._currentComplete = null;
complete(msg);
}
public waitForOne(): Promise<VSBuffer> {
return new Promise<VSBuffer>((complete) => {
this._currentComplete = complete;
this._trigger();
});
}
}
class EtherStream extends EventEmitter {
constructor(
private readonly _ether: Ether,
private readonly _name: 'a' | 'b'
) {
super();
}
write(data: Buffer, cb?: Function): boolean {
if (!Buffer.isBuffer(data)) {
throw new Error(`Invalid data`);
}
this._ether.write(this._name, data);
return true;
}
}
class Ether {
private readonly _a: EtherStream;
private readonly _b: EtherStream;
private _ab: Buffer[];
private _ba: Buffer[];
public get a(): Socket {
return <any>this._a;
}
public get b(): Socket {
return <any>this._b;
}
constructor() {
this._a = new EtherStream(this, 'a');
this._b = new EtherStream(this, 'b');
this._ab = [];
this._ba = [];
}
public write(from: 'a' | 'b', data: Buffer): void {
if (from === 'a') {
this._ab.push(data);
} else {
this._ba.push(data);
}
setImmediate(() => this._deliver());
}
private _deliver(): void {
if (this._ab.length > 0) {
const data = Buffer.concat(this._ab);
this._ab.length = 0;
this._b.emit('data', data);
setImmediate(() => this._deliver());
return;
}
if (this._ba.length > 0) {
const data = Buffer.concat(this._ba);
this._ba.length = 0;
this._a.emit('data', data);
setImmediate(() => this._deliver());
return;
}
}
}
suite('IPC, Socket Protocol', () => {
let ether: Ether;
setup(() => {
ether = new Ether();
});
test('read/write', async () => {
const a = new Protocol(new NodeSocket(ether.a));
const b = new Protocol(new NodeSocket(ether.b));
const bMessages = new MessageStream(b);
a.send(VSBuffer.fromString('foobarfarboo'));
const msg1 = await bMessages.waitForOne();
assert.equal(msg1.toString(), 'foobarfarboo');
const buffer = VSBuffer.alloc(1);
buffer.writeUInt8(123, 0);
a.send(buffer);
const msg2 = await bMessages.waitForOne();
assert.equal(msg2.readUInt8(0), 123);
});
test('read/write, object data', async () => {
const a = new Protocol(new NodeSocket(ether.a));
const b = new Protocol(new NodeSocket(ether.b));
const bMessages = new MessageStream(b);
const data = {
pi: Math.PI,
foo: 'bar',
more: true,
data: 'Hello World'.split('')
};
a.send(VSBuffer.fromString(JSON.stringify(data)));
const msg = await bMessages.waitForOne();
assert.deepEqual(JSON.parse(msg.toString()), data);
});
});
suite('PersistentProtocol reconnection', () => {
let ether: Ether;
setup(() => {
ether = new Ether();
});
test('acks get piggybacked with messages', async () => {
const a = new PersistentProtocol(new NodeSocket(ether.a));
const aMessages = new MessageStream(a);
const b = new PersistentProtocol(new NodeSocket(ether.b));
const bMessages = new MessageStream(b);
a.send(VSBuffer.fromString('a1'));
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 0);
a.send(VSBuffer.fromString('a2'));
assert.equal(a.unacknowledgedCount, 2);
assert.equal(b.unacknowledgedCount, 0);
a.send(VSBuffer.fromString('a3'));
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
const a1 = await bMessages.waitForOne();
assert.equal(a1.toString(), 'a1');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
const a2 = await bMessages.waitForOne();
assert.equal(a2.toString(), 'a2');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
const a3 = await bMessages.waitForOne();
assert.equal(a3.toString(), 'a3');
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 0);
b.send(VSBuffer.fromString('b1'));
assert.equal(a.unacknowledgedCount, 3);
assert.equal(b.unacknowledgedCount, 1);
const b1 = await aMessages.waitForOne();
assert.equal(b1.toString(), 'b1');
assert.equal(a.unacknowledgedCount, 0);
assert.equal(b.unacknowledgedCount, 1);
a.send(VSBuffer.fromString('a4'));
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 1);
const b2 = await bMessages.waitForOne();
assert.equal(b2.toString(), 'a4');
assert.equal(a.unacknowledgedCount, 1);
assert.equal(b.unacknowledgedCount, 0);
});
});
suite('IPC, create handle', () => {
test('createRandomIPCHandle', async () => {
return testIPCHandle(createRandomIPCHandle());
});
test('createStaticIPCHandle', async () => {
return testIPCHandle(createStaticIPCHandle(tmpdir(), 'test', product.version));
});
function testIPCHandle(handle: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
const pipeName = createRandomIPCHandle();
const server = createServer();
server.on('error', () => {
return new Promise(() => server.close(() => reject()));
});
server.listen(pipeName, () => {
server.removeListener('error', reject);
return new Promise(() => {
server.close(() => resolve());
});
});
});
}
});

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { TestChannel, TestService } from './testService';
const server = new Server('test');
const service = new TestService();
server.registerChannel('test', new TestChannel(service));

View File

@@ -0,0 +1,79 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event, Emitter } from 'vs/base/common/event';
import { timeout } from 'vs/base/common/async';
export interface IMarcoPoloEvent {
answer: string;
}
export interface ITestService {
onMarco: Event<IMarcoPoloEvent>;
marco(): Promise<string>;
pong(ping: string): Promise<{ incoming: string, outgoing: string }>;
cancelMe(): Promise<boolean>;
}
export class TestService implements ITestService {
private readonly _onMarco = new Emitter<IMarcoPoloEvent>();
onMarco: Event<IMarcoPoloEvent> = this._onMarco.event;
marco(): Promise<string> {
this._onMarco.fire({ answer: 'polo' });
return Promise.resolve('polo');
}
pong(ping: string): Promise<{ incoming: string, outgoing: string }> {
return Promise.resolve({ incoming: ping, outgoing: 'pong' });
}
cancelMe(): Promise<boolean> {
return Promise.resolve(timeout(100)).then(() => true);
}
}
export class TestChannel implements IServerChannel {
constructor(private testService: ITestService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'marco': return this.testService.onMarco;
}
throw new Error('Event not found');
}
call(_: unknown, command: string, ...args: any[]): Promise<any> {
switch (command) {
case 'pong': return this.testService.pong(args[0]);
case 'cancelMe': return this.testService.cancelMe();
case 'marco': return this.testService.marco();
default: return Promise.reject(new Error(`command not found: ${command}`));
}
}
}
export class TestServiceClient implements ITestService {
get onMarco(): Event<IMarcoPoloEvent> { return this.channel.listen('marco'); }
constructor(private channel: IChannel) { }
marco(): Promise<string> {
return this.channel.call('marco');
}
pong(ping: string): Promise<{ incoming: string, outgoing: string }> {
return this.channel.call('pong', ping);
}
cancelMe(): Promise<boolean> {
return this.channel.call('cancelMe');
}
}