Path: blob/main/src/vs/workbench/services/extensions/test/common/rpcProtocol.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { VSBuffer } from '../../../../../base/common/buffer.js';7import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';8import { Emitter, Event } from '../../../../../base/common/event.js';9import { DisposableStore } from '../../../../../base/common/lifecycle.js';10import { IMessagePassingProtocol } from '../../../../../base/parts/ipc/common/ipc.js';11import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';12import { ProxyIdentifier, SerializableObjectWithBuffers } from '../../common/proxyIdentifier.js';13import { RPCProtocol } from '../../common/rpcProtocol.js';1415suite('RPCProtocol', () => {1617let disposables: DisposableStore;1819class MessagePassingProtocol implements IMessagePassingProtocol {20private _pair?: MessagePassingProtocol;2122private readonly _onMessage = new Emitter<VSBuffer>();23public readonly onMessage: Event<VSBuffer> = this._onMessage.event;2425public setPair(other: MessagePassingProtocol) {26this._pair = other;27}2829public send(buffer: VSBuffer): void {30Promise.resolve().then(() => {31this._pair!._onMessage.fire(buffer);32});33}34}3536let delegate: (a1: any, a2: any) => any;37let bProxy: BClass;38class BClass {39$m(a1: any, a2: any): Promise<any> {40return Promise.resolve(delegate.call(null, a1, a2));41}42}4344setup(() => {45disposables = new DisposableStore();4647const a_protocol = new MessagePassingProtocol();48const b_protocol = new MessagePassingProtocol();49a_protocol.setPair(b_protocol);50b_protocol.setPair(a_protocol);5152const A = disposables.add(new RPCProtocol(a_protocol));53const B = disposables.add(new RPCProtocol(b_protocol));5455const bIdentifier = new ProxyIdentifier<BClass>('bb');56const bInstance = new BClass();57B.set(bIdentifier, bInstance);58bProxy = A.getProxy(bIdentifier);59});6061teardown(() => {62disposables.dispose();63});6465ensureNoDisposablesAreLeakedInTestSuite();6667test('simple call', function (done) {68delegate = (a1: number, a2: number) => a1 + a2;69bProxy.$m(4, 1).then((res: number) => {70assert.strictEqual(res, 5);71done(null);72}, done);73});7475test('simple call without result', function (done) {76delegate = (a1: number, a2: number) => { };77bProxy.$m(4, 1).then((res: number) => {78assert.strictEqual(res, undefined);79done(null);80}, done);81});8283test('passing buffer as argument', function (done) {84delegate = (a1: VSBuffer, a2: number) => {85assert.ok(a1 instanceof VSBuffer);86return a1.buffer[a2];87};88const b = VSBuffer.alloc(4);89b.buffer[0] = 1;90b.buffer[1] = 2;91b.buffer[2] = 3;92b.buffer[3] = 4;93bProxy.$m(b, 2).then((res: number) => {94assert.strictEqual(res, 3);95done(null);96}, done);97});9899test('returning a buffer', function (done) {100delegate = (a1: number, a2: number) => {101const b = VSBuffer.alloc(4);102b.buffer[0] = 1;103b.buffer[1] = 2;104b.buffer[2] = 3;105b.buffer[3] = 4;106return b;107};108bProxy.$m(4, 1).then((res: VSBuffer) => {109assert.ok(res instanceof VSBuffer);110assert.strictEqual(res.buffer[0], 1);111assert.strictEqual(res.buffer[1], 2);112assert.strictEqual(res.buffer[2], 3);113assert.strictEqual(res.buffer[3], 4);114done(null);115}, done);116});117118test('cancelling a call via CancellationToken before', function (done) {119delegate = (a1: number, a2: number) => a1 + a2;120const p = bProxy.$m(4, CancellationToken.Cancelled);121p.then((res: number) => {122assert.fail('should not receive result');123}, (err) => {124assert.ok(true);125done(null);126});127});128129test('passing CancellationToken.None', function (done) {130delegate = (a1: number, token: CancellationToken) => {131assert.ok(!!token);132return a1 + 1;133};134bProxy.$m(4, CancellationToken.None).then((res: number) => {135assert.strictEqual(res, 5);136done(null);137}, done);138});139140test('cancelling a call via CancellationToken quickly', function (done) {141// this is an implementation which, when cancellation is triggered, will return 7142delegate = (a1: number, token: CancellationToken) => {143return new Promise((resolve, reject) => {144const disposable = token.onCancellationRequested((e) => {145disposable.dispose();146resolve(7);147});148});149};150const tokenSource = new CancellationTokenSource();151const p = bProxy.$m(4, tokenSource.token);152p.then((res: number) => {153assert.strictEqual(res, 7);154}, (err) => {155assert.fail('should not receive error');156}).finally(done);157tokenSource.cancel();158});159160test('throwing an error', function (done) {161delegate = (a1: number, a2: number) => {162throw new Error(`nope`);163};164bProxy.$m(4, 1).then((res) => {165assert.fail('unexpected');166}, (err) => {167assert.strictEqual(err.message, 'nope');168}).finally(done);169});170171test('error promise', function (done) {172delegate = (a1: number, a2: number) => {173return Promise.reject(undefined);174};175bProxy.$m(4, 1).then((res) => {176assert.fail('unexpected');177}, (err) => {178assert.strictEqual(err, undefined);179}).finally(done);180});181182test('issue #60450: Converting circular structure to JSON', function (done) {183delegate = (a1: number, a2: number) => {184const circular = <any>{};185circular.self = circular;186return circular;187};188bProxy.$m(4, 1).then((res) => {189assert.strictEqual(res, null);190}, (err) => {191assert.fail('unexpected');192}).finally(done);193});194195test('issue #72798: null errors are hard to digest', function (done) {196delegate = (a1: number, a2: number) => {197// eslint-disable-next-line no-throw-literal198throw { 'what': 'what' };199};200bProxy.$m(4, 1).then((res) => {201assert.fail('unexpected');202}, (err) => {203assert.strictEqual(err.what, 'what');204}).finally(done);205});206207test('undefined arguments arrive as null', function () {208delegate = (a1: any, a2: any) => {209assert.strictEqual(typeof a1, 'undefined');210assert.strictEqual(a2, null);211return 7;212};213return bProxy.$m(undefined, null).then((res) => {214assert.strictEqual(res, 7);215});216});217218test('issue #81424: SerializeRequest should throw if an argument can not be serialized', () => {219const badObject = {};220(<any>badObject).loop = badObject;221222assert.throws(() => {223bProxy.$m(badObject, '2');224});225});226227test('SerializableObjectWithBuffers is correctly transfered', function (done) {228delegate = (a1: SerializableObjectWithBuffers<{ string: string; buff: VSBuffer }>, a2: number) => {229return new SerializableObjectWithBuffers({ string: a1.value.string + ' world', buff: a1.value.buff });230};231232const b = VSBuffer.alloc(4);233b.buffer[0] = 1;234b.buffer[1] = 2;235b.buffer[2] = 3;236b.buffer[3] = 4;237238bProxy.$m(new SerializableObjectWithBuffers({ string: 'hello', buff: b }), undefined).then((res: SerializableObjectWithBuffers<any>) => {239assert.ok(res instanceof SerializableObjectWithBuffers);240assert.strictEqual(res.value.string, 'hello world');241242assert.ok(res.value.buff instanceof VSBuffer);243244const bufferValues = Array.from(res.value.buff.buffer);245246assert.strictEqual(bufferValues[0], 1);247assert.strictEqual(bufferValues[1], 2);248assert.strictEqual(bufferValues[2], 3);249assert.strictEqual(bufferValues[3], 4);250done(null);251}, done);252});253});254255256