mirror of
https://github.com/coder/code-server.git
synced 2026-06-23 10:27:11 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
493
lib/vscode/src/vs/base/parts/ipc/test/common/ipc.test.ts
Normal file
493
lib/vscode/src/vs/base/parts/ipc/test/common/ipc.test.ts
Normal file
@@ -0,0 +1,493 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
class QueueProtocol implements IMessagePassingProtocol {
|
||||
|
||||
private buffering = true;
|
||||
private buffers: VSBuffer[] = [];
|
||||
|
||||
private readonly _onMessage = new Emitter<VSBuffer>({
|
||||
onFirstListenerDidAdd: () => {
|
||||
for (const buffer of this.buffers) {
|
||||
this._onMessage.fire(buffer);
|
||||
}
|
||||
|
||||
this.buffers = [];
|
||||
this.buffering = false;
|
||||
},
|
||||
onLastListenerRemove: () => {
|
||||
this.buffering = true;
|
||||
}
|
||||
});
|
||||
|
||||
readonly onMessage = this._onMessage.event;
|
||||
other!: QueueProtocol;
|
||||
|
||||
send(buffer: VSBuffer): void {
|
||||
this.other.receive(buffer);
|
||||
}
|
||||
|
||||
protected receive(buffer: VSBuffer): void {
|
||||
if (this.buffering) {
|
||||
this.buffers.push(buffer);
|
||||
} else {
|
||||
this._onMessage.fire(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createProtocolPair(): [IMessagePassingProtocol, IMessagePassingProtocol] {
|
||||
const one = new QueueProtocol();
|
||||
const other = new QueueProtocol();
|
||||
one.other = other;
|
||||
other.other = one;
|
||||
|
||||
return [one, other];
|
||||
}
|
||||
|
||||
class TestIPCClient extends IPCClient<string> {
|
||||
|
||||
private readonly _onDidDisconnect = new Emitter<void>();
|
||||
readonly onDidDisconnect = this._onDidDisconnect.event;
|
||||
|
||||
constructor(protocol: IMessagePassingProtocol, id: string) {
|
||||
super(protocol, id);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._onDidDisconnect.fire();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class TestIPCServer extends IPCServer<string> {
|
||||
|
||||
private readonly onDidClientConnect: Emitter<ClientConnectionEvent>;
|
||||
|
||||
constructor() {
|
||||
const onDidClientConnect = new Emitter<ClientConnectionEvent>();
|
||||
super(onDidClientConnect.event);
|
||||
this.onDidClientConnect = onDidClientConnect;
|
||||
}
|
||||
|
||||
createConnection(id: string): IPCClient<string> {
|
||||
const [pc, ps] = createProtocolPair();
|
||||
const client = new TestIPCClient(pc, id);
|
||||
|
||||
this.onDidClientConnect.fire({
|
||||
protocol: ps,
|
||||
onDidClientDisconnect: client.onDidDisconnect
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
const TestChannelId = 'testchannel';
|
||||
|
||||
interface ITestService {
|
||||
marco(): Promise<string>;
|
||||
error(message: string): Promise<void>;
|
||||
neverComplete(): Promise<void>;
|
||||
neverCompleteCT(cancellationToken: CancellationToken): Promise<void>;
|
||||
buffersLength(buffers: VSBuffer[]): Promise<number>;
|
||||
marshall(uri: URI): Promise<URI>;
|
||||
context(): Promise<unknown>;
|
||||
|
||||
onPong: Event<string>;
|
||||
}
|
||||
|
||||
class TestService implements ITestService {
|
||||
|
||||
private readonly _onPong = new Emitter<string>();
|
||||
readonly onPong = this._onPong.event;
|
||||
|
||||
marco(): Promise<string> {
|
||||
return Promise.resolve('polo');
|
||||
}
|
||||
|
||||
error(message: string): Promise<void> {
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
|
||||
neverComplete(): Promise<void> {
|
||||
return new Promise(_ => { });
|
||||
}
|
||||
|
||||
neverCompleteCT(cancellationToken: CancellationToken): Promise<void> {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
return Promise.reject(canceled());
|
||||
}
|
||||
|
||||
return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled())));
|
||||
}
|
||||
|
||||
buffersLength(buffers: VSBuffer[]): Promise<number> {
|
||||
return Promise.resolve(buffers.reduce((r, b) => r + b.buffer.length, 0));
|
||||
}
|
||||
|
||||
ping(msg: string): void {
|
||||
this._onPong.fire(msg);
|
||||
}
|
||||
|
||||
marshall(uri: URI): Promise<URI> {
|
||||
return Promise.resolve(uri);
|
||||
}
|
||||
|
||||
context(context?: unknown): Promise<unknown> {
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
}
|
||||
|
||||
class TestChannel implements IServerChannel {
|
||||
|
||||
constructor(private service: ITestService) { }
|
||||
|
||||
call(_: unknown, command: string, arg: any, cancellationToken: CancellationToken): Promise<any> {
|
||||
switch (command) {
|
||||
case 'marco': return this.service.marco();
|
||||
case 'error': return this.service.error(arg);
|
||||
case 'neverComplete': return this.service.neverComplete();
|
||||
case 'neverCompleteCT': return this.service.neverCompleteCT(cancellationToken);
|
||||
case 'buffersLength': return this.service.buffersLength(arg);
|
||||
default: return Promise.reject(new Error('not implemented'));
|
||||
}
|
||||
}
|
||||
|
||||
listen(_: unknown, event: string, arg?: any): Event<any> {
|
||||
switch (event) {
|
||||
case 'onPong': return this.service.onPong;
|
||||
default: throw new Error('not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestChannelClient implements ITestService {
|
||||
|
||||
get onPong(): Event<string> {
|
||||
return this.channel.listen('onPong');
|
||||
}
|
||||
|
||||
constructor(private channel: IChannel) { }
|
||||
|
||||
marco(): Promise<string> {
|
||||
return this.channel.call('marco');
|
||||
}
|
||||
|
||||
error(message: string): Promise<void> {
|
||||
return this.channel.call('error', message);
|
||||
}
|
||||
|
||||
neverComplete(): Promise<void> {
|
||||
return this.channel.call('neverComplete');
|
||||
}
|
||||
|
||||
neverCompleteCT(cancellationToken: CancellationToken): Promise<void> {
|
||||
return this.channel.call('neverCompleteCT', undefined, cancellationToken);
|
||||
}
|
||||
|
||||
buffersLength(buffers: VSBuffer[]): Promise<number> {
|
||||
return this.channel.call('buffersLength', buffers);
|
||||
}
|
||||
|
||||
marshall(uri: URI): Promise<URI> {
|
||||
return this.channel.call('marshall', uri);
|
||||
}
|
||||
|
||||
context(): Promise<unknown> {
|
||||
return this.channel.call('context');
|
||||
}
|
||||
}
|
||||
|
||||
suite('Base IPC', function () {
|
||||
|
||||
test('createProtocolPair', async function () {
|
||||
const [clientProtocol, serverProtocol] = createProtocolPair();
|
||||
|
||||
const b1 = VSBuffer.alloc(0);
|
||||
clientProtocol.send(b1);
|
||||
|
||||
const b3 = VSBuffer.alloc(0);
|
||||
serverProtocol.send(b3);
|
||||
|
||||
const b2 = await Event.toPromise(serverProtocol.onMessage);
|
||||
const b4 = await Event.toPromise(clientProtocol.onMessage);
|
||||
|
||||
assert.strictEqual(b1, b2);
|
||||
assert.strictEqual(b3, b4);
|
||||
});
|
||||
|
||||
suite('one to one', function () {
|
||||
let server: IPCServer;
|
||||
let client: IPCClient;
|
||||
let service: TestService;
|
||||
let ipcService: ITestService;
|
||||
|
||||
setup(function () {
|
||||
service = new TestService();
|
||||
const testServer = new TestIPCServer();
|
||||
server = testServer;
|
||||
|
||||
server.registerChannel(TestChannelId, new TestChannel(service));
|
||||
|
||||
client = testServer.createConnection('client1');
|
||||
ipcService = new TestChannelClient(client.getChannel(TestChannelId));
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
client.dispose();
|
||||
server.dispose();
|
||||
});
|
||||
|
||||
test('call success', async function () {
|
||||
const r = await ipcService.marco();
|
||||
return assert.equal(r, 'polo');
|
||||
});
|
||||
|
||||
test('call error', async function () {
|
||||
try {
|
||||
await ipcService.error('nice error');
|
||||
return assert.fail('should not reach here');
|
||||
} catch (err) {
|
||||
return assert.equal(err.message, 'nice error');
|
||||
}
|
||||
});
|
||||
|
||||
test('cancel call with cancelled cancellation token', async function () {
|
||||
try {
|
||||
await ipcService.neverCompleteCT(CancellationToken.Cancelled);
|
||||
return assert.fail('should not reach here');
|
||||
} catch (err) {
|
||||
return assert(err.message === 'Canceled');
|
||||
}
|
||||
});
|
||||
|
||||
test('cancel call with cancellation token (sync)', function () {
|
||||
const cts = new CancellationTokenSource();
|
||||
const promise = ipcService.neverCompleteCT(cts.token).then(
|
||||
_ => assert.fail('should not reach here'),
|
||||
err => assert(err.message === 'Canceled')
|
||||
);
|
||||
|
||||
cts.cancel();
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
test('cancel call with cancellation token (async)', function () {
|
||||
const cts = new CancellationTokenSource();
|
||||
const promise = ipcService.neverCompleteCT(cts.token).then(
|
||||
_ => assert.fail('should not reach here'),
|
||||
err => assert(err.message === 'Canceled')
|
||||
);
|
||||
|
||||
setTimeout(() => cts.cancel());
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
test('listen to events', async function () {
|
||||
const messages: string[] = [];
|
||||
|
||||
ipcService.onPong(msg => messages.push(msg));
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, []);
|
||||
service.ping('hello');
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, ['hello']);
|
||||
service.ping('world');
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, ['hello', 'world']);
|
||||
});
|
||||
|
||||
test('buffers in arrays', async function () {
|
||||
const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
|
||||
return assert.equal(r, 5);
|
||||
});
|
||||
});
|
||||
|
||||
suite('one to one (proxy)', function () {
|
||||
let server: IPCServer;
|
||||
let client: IPCClient;
|
||||
let service: TestService;
|
||||
let ipcService: ITestService;
|
||||
|
||||
setup(function () {
|
||||
service = new TestService();
|
||||
const testServer = new TestIPCServer();
|
||||
server = testServer;
|
||||
|
||||
server.registerChannel(TestChannelId, createChannelReceiver(service));
|
||||
|
||||
client = testServer.createConnection('client1');
|
||||
ipcService = createChannelSender(client.getChannel(TestChannelId));
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
client.dispose();
|
||||
server.dispose();
|
||||
});
|
||||
|
||||
test('call success', async function () {
|
||||
const r = await ipcService.marco();
|
||||
return assert.equal(r, 'polo');
|
||||
});
|
||||
|
||||
test('call error', async function () {
|
||||
try {
|
||||
await ipcService.error('nice error');
|
||||
return assert.fail('should not reach here');
|
||||
} catch (err) {
|
||||
return assert.equal(err.message, 'nice error');
|
||||
}
|
||||
});
|
||||
|
||||
test('listen to events', async function () {
|
||||
const messages: string[] = [];
|
||||
|
||||
ipcService.onPong(msg => messages.push(msg));
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, []);
|
||||
service.ping('hello');
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, ['hello']);
|
||||
service.ping('world');
|
||||
await timeout(0);
|
||||
|
||||
assert.deepEqual(messages, ['hello', 'world']);
|
||||
});
|
||||
|
||||
test('marshalling uri', async function () {
|
||||
const uri = URI.file('foobar');
|
||||
const r = await ipcService.marshall(uri);
|
||||
assert.ok(r instanceof URI);
|
||||
return assert.ok(isEqual(r, uri));
|
||||
});
|
||||
|
||||
test('buffers in arrays', async function () {
|
||||
const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
|
||||
return assert.equal(r, 5);
|
||||
});
|
||||
});
|
||||
|
||||
suite('one to one (proxy, extra context)', function () {
|
||||
let server: IPCServer;
|
||||
let client: IPCClient;
|
||||
let service: TestService;
|
||||
let ipcService: ITestService;
|
||||
|
||||
setup(function () {
|
||||
service = new TestService();
|
||||
const testServer = new TestIPCServer();
|
||||
server = testServer;
|
||||
|
||||
server.registerChannel(TestChannelId, createChannelReceiver(service));
|
||||
|
||||
client = testServer.createConnection('client1');
|
||||
ipcService = createChannelSender(client.getChannel(TestChannelId), { context: 'Super Context' });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
client.dispose();
|
||||
server.dispose();
|
||||
});
|
||||
|
||||
test('call extra context', async function () {
|
||||
const r = await ipcService.context();
|
||||
return assert.equal(r, 'Super Context');
|
||||
});
|
||||
});
|
||||
|
||||
suite('one to many', function () {
|
||||
test('all clients get pinged', async function () {
|
||||
const service = new TestService();
|
||||
const channel = new TestChannel(service);
|
||||
const server = new TestIPCServer();
|
||||
server.registerChannel('channel', channel);
|
||||
|
||||
let client1GotPinged = false;
|
||||
const client1 = server.createConnection('client1');
|
||||
const ipcService1 = new TestChannelClient(client1.getChannel('channel'));
|
||||
ipcService1.onPong(() => client1GotPinged = true);
|
||||
|
||||
let client2GotPinged = false;
|
||||
const client2 = server.createConnection('client2');
|
||||
const ipcService2 = new TestChannelClient(client2.getChannel('channel'));
|
||||
ipcService2.onPong(() => client2GotPinged = true);
|
||||
|
||||
await timeout(1);
|
||||
service.ping('hello');
|
||||
|
||||
await timeout(1);
|
||||
assert(client1GotPinged, 'client 1 got pinged');
|
||||
assert(client2GotPinged, 'client 2 got pinged');
|
||||
|
||||
client1.dispose();
|
||||
client2.dispose();
|
||||
server.dispose();
|
||||
});
|
||||
|
||||
test('server gets pings from all clients (broadcast channel)', async function () {
|
||||
const server = new TestIPCServer();
|
||||
|
||||
const client1 = server.createConnection('client1');
|
||||
const clientService1 = new TestService();
|
||||
const clientChannel1 = new TestChannel(clientService1);
|
||||
client1.registerChannel('channel', clientChannel1);
|
||||
|
||||
const pings: string[] = [];
|
||||
const channel = server.getChannel('channel', () => true);
|
||||
const service = new TestChannelClient(channel);
|
||||
service.onPong(msg => pings.push(msg));
|
||||
|
||||
await timeout(1);
|
||||
clientService1.ping('hello 1');
|
||||
|
||||
await timeout(1);
|
||||
assert.deepEqual(pings, ['hello 1']);
|
||||
|
||||
const client2 = server.createConnection('client2');
|
||||
const clientService2 = new TestService();
|
||||
const clientChannel2 = new TestChannel(clientService2);
|
||||
client2.registerChannel('channel', clientChannel2);
|
||||
|
||||
await timeout(1);
|
||||
clientService2.ping('hello 2');
|
||||
|
||||
await timeout(1);
|
||||
assert.deepEqual(pings, ['hello 1', 'hello 2']);
|
||||
|
||||
client1.dispose();
|
||||
clientService1.ping('hello 1');
|
||||
|
||||
await timeout(1);
|
||||
assert.deepEqual(pings, ['hello 1', 'hello 2']);
|
||||
|
||||
await timeout(1);
|
||||
clientService2.ping('hello again 2');
|
||||
|
||||
await timeout(1);
|
||||
assert.deepEqual(pings, ['hello 1', 'hello 2', 'hello again 2']);
|
||||
|
||||
client2.dispose();
|
||||
server.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
78
lib/vscode/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts
Normal file
78
lib/vscode/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts
Normal 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());
|
||||
});
|
||||
});
|
||||
258
lib/vscode/src/vs/base/parts/ipc/test/node/ipc.net.test.ts
Normal file
258
lib/vscode/src/vs/base/parts/ipc/test/node/ipc.net.test.ts
Normal 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());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
11
lib/vscode/src/vs/base/parts/ipc/test/node/testApp.ts
Normal file
11
lib/vscode/src/vs/base/parts/ipc/test/node/testApp.ts
Normal 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));
|
||||
79
lib/vscode/src/vs/base/parts/ipc/test/node/testService.ts
Normal file
79
lib/vscode/src/vs/base/parts/ipc/test/node/testService.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user