diff options
Diffstat (limited to 'tests/test.rfb.js')
-rw-r--r-- | tests/test.rfb.js | 455 |
1 files changed, 439 insertions, 16 deletions
diff --git a/tests/test.rfb.js b/tests/test.rfb.js index ba35b9c..bb690ce 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2,7 +2,11 @@ const expect = chai.expect; import RFB from '../core/rfb.js'; import Websock from '../core/websock.js'; +import ZStream from "../vendor/pako/lib/zlib/zstream.js"; +import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js"; import { encodings } from '../core/encodings.js'; +import { toUnsigned32bit } from '../core/util/int.js'; +import { encodeUTF8 } from '../core/util/strings.js'; import FakeWebSocket from './fake.websocket.js'; @@ -48,6 +52,35 @@ function pushString(arr, string) { } } +function deflateWithSize(data) { + // Adds the size of the string in front before deflating + + let unCompData = []; + unCompData.push((data.length >> 24) & 0xFF, + (data.length >> 16) & 0xFF, + (data.length >> 8) & 0xFF, + (data.length & 0xFF)); + + for (let i = 0; i < data.length; i++) { + unCompData.push(data.charCodeAt(i)); + } + + let strm = new ZStream(); + let chunkSize = 1024 * 10 * 10; + strm.output = new Uint8Array(chunkSize); + deflateInit(strm, 5); + + strm.input = unCompData; + strm.avail_in = strm.input.length; + strm.next_in = 0; + strm.next_out = 0; + strm.avail_out = chunkSize; + + deflate(strm, 3); + + return new Uint8Array(strm.output.buffer, 0, strm.next_out); +} + describe('Remote Frame Buffer Protocol Client', function () { let clock; let raf; @@ -291,12 +324,39 @@ describe('Remote Frame Buffer Protocol Client', function () { }); describe('#clipboardPasteFrom', function () { - it('should send the given text in a paste event', function () { - const expected = {_sQ: new Uint8Array(11), _sQlen: 0, - _sQbufferSize: 11, flush: () => {}}; - RFB.messages.clientCutText(expected, 'abc'); - client.clipboardPasteFrom('abc'); - expect(client._sock).to.have.sent(expected._sQ); + describe('Clipboard update handling', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'clientCutText'); + sinon.spy(RFB.messages, 'extendedClipboardNotify'); + }); + + afterEach(function () { + RFB.messages.clientCutText.restore(); + RFB.messages.extendedClipboardNotify.restore(); + }); + + it('should send the given text in an clipboard update', function () { + client.clipboardPasteFrom('abc'); + + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(client._sock, + new Uint8Array([97, 98, 99])); + }); + + it('should send an notify if extended clipboard is supported by server', function () { + // Send our capabilities + let data = [3, 0, 0, 0]; + const flags = [0x1F, 0x00, 0x00, 0x01]; + let fileSizes = [0x00, 0x00, 0x00, 0x1E]; + + push32(data, toUnsigned32bit(-8)); + data = data.concat(flags); + data = data.concat(fileSizes); + client._sock._websocket._receive_data(new Uint8Array(data)); + + client.clipboardPasteFrom('extended test'); + expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; + }); }); it('should flush multiple times for large clipboards', function () { @@ -2336,17 +2396,217 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - it('should fire the clipboard callback with the retrieved text on ServerCutText', function () { - const expected_str = 'cheese!'; - const data = [3, 0, 0, 0]; - push32(data, expected_str.length); - for (let i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); } - const spy = sinon.spy(); - client.addEventListener("clipboard", spy); + describe('Normal Clipboard Handling Receive', function () { + it('should fire the clipboard callback with the retrieved text on ServerCutText', function () { + const expected_str = 'cheese!'; + const data = [3, 0, 0, 0]; + push32(data, expected_str.length); + for (let i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); } + const spy = sinon.spy(); + client.addEventListener("clipboard", spy); + + client._sock._websocket._receive_data(new Uint8Array(data)); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.text).to.equal(expected_str); + }); + }); + + describe('Extended clipboard Handling', function () { + + describe('Extended clipboard initialization', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'extendedClipboardCaps'); + }); + + afterEach(function () { + RFB.messages.extendedClipboardCaps.restore(); + }); + + it('should update capabilities when receiving a Caps message', function () { + let data = [3, 0, 0, 0]; + const flags = [0x1F, 0x00, 0x00, 0x03]; + let fileSizes = [0x00, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x3C]; + + push32(data, toUnsigned32bit(-12)); + data = data.concat(flags); + data = data.concat(fileSizes); + client._sock._websocket._receive_data(new Uint8Array(data)); + + // Check that we give an response caps when we receive one + expect(RFB.messages.extendedClipboardCaps).to.have.been.calledOnce; + + // FIXME: Can we avoid checking internal variables? + expect(client._clipboardServerCapabilitiesFormats[0]).to.not.equal(true); + expect(client._clipboardServerCapabilitiesFormats[1]).to.equal(true); + expect(client._clipboardServerCapabilitiesFormats[2]).to.equal(true); + expect(client._clipboardServerCapabilitiesActions[(1 << 24)]).to.equal(true); + }); + + + }); + + describe('Extended Clipboard Handling Receive', function () { + + beforeEach(function () { + // Send our capabilities + let data = [3, 0, 0, 0]; + const flags = [0x1F, 0x00, 0x00, 0x01]; + let fileSizes = [0x00, 0x00, 0x00, 0x1E]; + + push32(data, toUnsigned32bit(-8)); + data = data.concat(flags); + data = data.concat(fileSizes); + client._sock._websocket._receive_data(new Uint8Array(data)); + }); + + describe('Handle Provide', function () { + it('should update clipboard with correct Unicode data from a Provide message', function () { + let expectedData = "Aå漢字!"; + let data = [3, 0, 0, 0]; + const flags = [0x10, 0x00, 0x00, 0x01]; + + /* The size 10 (utf8 encoded string size) and the + string "Aå漢字!" utf8 encoded and deflated. */ + let deflatedData = [120, 94, 99, 96, 96, 224, 114, 60, + 188, 244, 217, 158, 69, 79, 215, + 78, 87, 4, 0, 35, 207, 6, 66]; + + // How much data we are sending. + push32(data, toUnsigned32bit(-(4 + deflatedData.length))); + + data = data.concat(flags); + data = data.concat(deflatedData); + + const spy = sinon.spy(); + client.addEventListener("clipboard", spy); + + client._sock._websocket._receive_data(new Uint8Array(data)); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.text).to.equal(expectedData); + client.removeEventListener("clipboard", spy); + }); + + it('should update clipboard with correct escape characters from a Provide message ', function () { + let expectedData = "Oh\nmy!"; + let data = [3, 0, 0, 0]; + const flags = [0x10, 0x00, 0x00, 0x01]; + + let text = encodeUTF8("Oh\r\nmy!\0"); + + let deflatedText = deflateWithSize(text); + + // How much data we are sending. + push32(data, toUnsigned32bit(-(4 + deflatedText.length))); + + data = data.concat(flags); + + let sendData = new Uint8Array(data.length + deflatedText.length); + sendData.set(data); + sendData.set(deflatedText, data.length); + + const spy = sinon.spy(); + client.addEventListener("clipboard", spy); + + client._sock._websocket._receive_data(sendData); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.text).to.equal(expectedData); + client.removeEventListener("clipboard", spy); + }); + + }); + + describe('Handle Notify', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'extendedClipboardRequest'); + }); + + afterEach(function () { + RFB.messages.extendedClipboardRequest.restore(); + }); + + it('should make a request with supported formats when receiving a notify message', function () { + let data = [3, 0, 0, 0]; + const flags = [0x08, 0x00, 0x00, 0x07]; + push32(data, toUnsigned32bit(-4)); + data = data.concat(flags); + let expectedData = [0x01]; + + client._sock._websocket._receive_data(new Uint8Array(data)); + + expect(RFB.messages.extendedClipboardRequest).to.have.been.calledOnce; + expect(RFB.messages.extendedClipboardRequest).to.have.been.calledWith(client._sock, expectedData); + }); + }); + + describe('Handle Peek', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'extendedClipboardNotify'); + }); + + afterEach(function () { + RFB.messages.extendedClipboardNotify.restore(); + }); + + it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () { + let data = [3, 0, 0, 0]; + const flags = [0x04, 0x00, 0x00, 0x00]; + push32(data, toUnsigned32bit(-4)); + data = data.concat(flags); + let expectedData = []; + + client._sock._websocket._receive_data(new Uint8Array(data)); + + expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; + expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData); + }); + + it('should send a Notify message with supported formats when receiving a Peek', function () { + let data = [3, 0, 0, 0]; + const flags = [0x04, 0x00, 0x00, 0x00]; + push32(data, toUnsigned32bit(-4)); + data = data.concat(flags); + let expectedData = [0x01]; + + // Needed to have clipboard data to read. + // This will trigger a call to Notify, reset history + client.clipboardPasteFrom("HejHej"); + RFB.messages.extendedClipboardNotify.resetHistory(); + + client._sock._websocket._receive_data(new Uint8Array(data)); + + expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; + expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData); + }); + }); + + describe('Handle Request', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'extendedClipboardProvide'); + }); + + afterEach(function () { + RFB.messages.extendedClipboardProvide.restore(); + }); + + it('should send a Provide message with supported formats when receiving a Request', function () { + let data = [3, 0, 0, 0]; + const flags = [0x02, 0x00, 0x00, 0x01]; + push32(data, toUnsigned32bit(-4)); + data = data.concat(flags); + let expectedData = [0x01]; + + client.clipboardPasteFrom("HejHej"); + expect(RFB.messages.extendedClipboardProvide).to.not.have.been.called; + + client._sock._websocket._receive_data(new Uint8Array(data)); + + expect(RFB.messages.extendedClipboardProvide).to.have.been.calledOnce; + expect(RFB.messages.extendedClipboardProvide).to.have.been.calledWith(client._sock, expectedData, ["HejHej"]); + }); + }); + }); - client._sock._websocket._receive_data(new Uint8Array(data)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.text).to.equal(expected_str); }); it('should fire the bell callback on Bell', function () { @@ -2574,3 +2834,166 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); }); + +describe('RFB messages', function () { + let sock; + + before(function () { + FakeWebSocket.replace(); + sock = new Websock(); + sock.open(); + }); + + after(function () { + FakeWebSocket.restore(); + }); + + describe('Extended Clipboard Handling Send', function () { + beforeEach(function () { + sinon.spy(RFB.messages, 'clientCutText'); + }); + + afterEach(function () { + RFB.messages.clientCutText.restore(); + }); + + it('should call clientCutText with correct Caps data', function () { + let formats = { + 0: 2, + 2: 4121 + }; + let expectedData = new Uint8Array([0x1F, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x10, 0x19]); + let actions = [ + 1 << 24, // Caps + 1 << 25, // Request + 1 << 26, // Peek + 1 << 27, // Notify + 1 << 28 // Provide + ]; + + RFB.messages.extendedClipboardCaps(sock, actions, formats); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + }); + + it('should call clientCutText with correct Request data', function () { + let formats = new Uint8Array([0x01]); + let expectedData = new Uint8Array([0x02, 0x00, 0x00, 0x01]); + + RFB.messages.extendedClipboardRequest(sock, formats); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + }); + + it('should call clientCutText with correct Notify data', function () { + let formats = new Uint8Array([0x01]); + let expectedData = new Uint8Array([0x08, 0x00, 0x00, 0x01]); + + RFB.messages.extendedClipboardNotify(sock, formats); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + }); + + it('should call clientCutText with correct Provide data', function () { + let testText = "Test string"; + let expectedText = encodeUTF8(testText + "\0"); + + let deflatedData = deflateWithSize(expectedText); + + // Build Expected with flags and deflated data + let expectedData = new Uint8Array(4 + deflatedData.length); + expectedData[0] = 0x10; // The client capabilities + expectedData[1] = 0x00; // Reserved flags + expectedData[2] = 0x00; // Reserved flags + expectedData[3] = 0x01; // The formats client supports + expectedData.set(deflatedData, 4); + + RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + }); + + describe('End of line characters', function () { + it('Carriage return', function () { + + let testText = "Hello\rworld\r\r!"; + let expectedText = encodeUTF8("Hello\r\nworld\r\n\r\n!\0"); + + let deflatedData = deflateWithSize(expectedText); + + // Build Expected with flags and deflated data + let expectedData = new Uint8Array(4 + deflatedData.length); + expectedData[0] = 0x10; // The client capabilities + expectedData[1] = 0x00; // Reserved flags + expectedData[2] = 0x00; // Reserved flags + expectedData[3] = 0x01; // The formats client supports + expectedData.set(deflatedData, 4); + + RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + }); + + it('Carriage return Line feed', function () { + + let testText = "Hello\r\n\r\nworld\r\n!"; + let expectedText = encodeUTF8(testText + "\0"); + + let deflatedData = deflateWithSize(expectedText); + + // Build Expected with flags and deflated data + let expectedData = new Uint8Array(4 + deflatedData.length); + expectedData[0] = 0x10; // The client capabilities + expectedData[1] = 0x00; // Reserved flags + expectedData[2] = 0x00; // Reserved flags + expectedData[3] = 0x01; // The formats client supports + expectedData.set(deflatedData, 4); + + RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + }); + + it('Line feed', function () { + let testText = "Hello\n\n\nworld\n!"; + let expectedText = encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0"); + + let deflatedData = deflateWithSize(expectedText); + + // Build Expected with flags and deflated data + let expectedData = new Uint8Array(4 + deflatedData.length); + expectedData[0] = 0x10; // The client capabilities + expectedData[1] = 0x00; // Reserved flags + expectedData[2] = 0x00; // Reserved flags + expectedData[3] = 0x01; // The formats client supports + expectedData.set(deflatedData, 4); + + RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + }); + + it('Carriage return and Line feed mixed', function () { + let testText = "\rHello\r\n\rworld\n\n!"; + let expectedText = encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0"); + + let deflatedData = deflateWithSize(expectedText); + + // Build Expected with flags and deflated data + let expectedData = new Uint8Array(4 + deflatedData.length); + expectedData[0] = 0x10; // The client capabilities + expectedData[1] = 0x00; // Reserved flags + expectedData[2] = 0x00; // Reserved flags + expectedData[3] = 0x01; // The formats client supports + expectedData.set(deflatedData, 4); + + RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); + expect(RFB.messages.clientCutText).to.have.been.calledOnce; + expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + }); + }); + }); +}); |