test.websock.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // requires local modules: websock, util
  2. // requires test modules: fake.websocket, assertions
  3. /* jshint expr: true */
  4. var assert = chai.assert;
  5. var expect = chai.expect;
  6. describe('Websock', function() {
  7. "use strict";
  8. describe('Queue methods', function () {
  9. var sock;
  10. var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
  11. beforeEach(function () {
  12. sock = new Websock();
  13. // skip init
  14. sock._allocate_buffers();
  15. sock._rQ.set(RQ_TEMPLATE);
  16. sock._rQlen = RQ_TEMPLATE.length;
  17. });
  18. describe('rQlen', function () {
  19. it('should return the length of the receive queue', function () {
  20. sock.set_rQi(0);
  21. expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length);
  22. });
  23. it("should return the proper length if we read some from the receive queue", function () {
  24. sock.set_rQi(1);
  25. expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length - 1);
  26. });
  27. });
  28. describe('rQpeek8', function () {
  29. it('should peek at the next byte without poping it off the queue', function () {
  30. var bef_len = sock.rQlen();
  31. var peek = sock.rQpeek8();
  32. expect(sock.rQpeek8()).to.equal(peek);
  33. expect(sock.rQlen()).to.equal(bef_len);
  34. });
  35. });
  36. describe('rQshift8', function () {
  37. it('should pop a single byte from the receive queue', function () {
  38. var peek = sock.rQpeek8();
  39. var bef_len = sock.rQlen();
  40. expect(sock.rQshift8()).to.equal(peek);
  41. expect(sock.rQlen()).to.equal(bef_len - 1);
  42. });
  43. });
  44. describe('rQshift16', function () {
  45. it('should pop two bytes from the receive queue and return a single number', function () {
  46. var bef_len = sock.rQlen();
  47. var expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
  48. expect(sock.rQshift16()).to.equal(expected);
  49. expect(sock.rQlen()).to.equal(bef_len - 2);
  50. });
  51. });
  52. describe('rQshift32', function () {
  53. it('should pop four bytes from the receive queue and return a single number', function () {
  54. var bef_len = sock.rQlen();
  55. var expected = (RQ_TEMPLATE[0] << 24) +
  56. (RQ_TEMPLATE[1] << 16) +
  57. (RQ_TEMPLATE[2] << 8) +
  58. RQ_TEMPLATE[3];
  59. expect(sock.rQshift32()).to.equal(expected);
  60. expect(sock.rQlen()).to.equal(bef_len - 4);
  61. });
  62. });
  63. describe('rQshiftStr', function () {
  64. it('should shift the given number of bytes off of the receive queue and return a string', function () {
  65. var bef_len = sock.rQlen();
  66. var bef_rQi = sock.get_rQi();
  67. var shifted = sock.rQshiftStr(3);
  68. expect(shifted).to.be.a('string');
  69. expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
  70. expect(sock.rQlen()).to.equal(bef_len - 3);
  71. });
  72. it('should shift the entire rest of the queue off if no length is given', function () {
  73. sock.rQshiftStr();
  74. expect(sock.rQlen()).to.equal(0);
  75. });
  76. });
  77. describe('rQshiftBytes', function () {
  78. it('should shift the given number of bytes of the receive queue and return an array', function () {
  79. var bef_len = sock.rQlen();
  80. var bef_rQi = sock.get_rQi();
  81. var shifted = sock.rQshiftBytes(3);
  82. expect(shifted).to.be.an.instanceof(Uint8Array);
  83. expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
  84. expect(sock.rQlen()).to.equal(bef_len - 3);
  85. });
  86. it('should shift the entire rest of the queue off if no length is given', function () {
  87. sock.rQshiftBytes();
  88. expect(sock.rQlen()).to.equal(0);
  89. });
  90. });
  91. describe('rQslice', function () {
  92. beforeEach(function () {
  93. sock.set_rQi(0);
  94. });
  95. it('should not modify the receive queue', function () {
  96. var bef_len = sock.rQlen();
  97. sock.rQslice(0, 2);
  98. expect(sock.rQlen()).to.equal(bef_len);
  99. });
  100. it('should return an array containing the given slice of the receive queue', function () {
  101. var sl = sock.rQslice(0, 2);
  102. expect(sl).to.be.an.instanceof(Uint8Array);
  103. expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
  104. });
  105. it('should use the rest of the receive queue if no end is given', function () {
  106. var sl = sock.rQslice(1);
  107. expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
  108. expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
  109. });
  110. it('should take the current rQi in to account', function () {
  111. sock.set_rQi(1);
  112. expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
  113. });
  114. });
  115. describe('rQwait', function () {
  116. beforeEach(function () {
  117. sock.set_rQi(0);
  118. });
  119. it('should return true if there are not enough bytes in the receive queue', function () {
  120. expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
  121. });
  122. it('should return false if there are enough bytes in the receive queue', function () {
  123. expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
  124. });
  125. it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
  126. sock.set_rQi(5);
  127. expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
  128. expect(sock.get_rQi()).to.equal(1);
  129. });
  130. it('should raise an error if we try to go back more than possible', function () {
  131. sock.set_rQi(5);
  132. expect(function () { sock.rQwait('hi', RQ_TEMPLATE.length, 6); }).to.throw(Error);
  133. });
  134. it('should not reduce rQi if there are enough bytes', function () {
  135. sock.set_rQi(5);
  136. sock.rQwait('hi', 1, 6);
  137. expect(sock.get_rQi()).to.equal(5);
  138. });
  139. });
  140. describe('flush', function () {
  141. beforeEach(function () {
  142. sock._websocket = {
  143. send: sinon.spy()
  144. };
  145. });
  146. it('should actually send on the websocket if the websocket does not have too much buffered', function () {
  147. sock.maxBufferedAmount = 10;
  148. sock._websocket.bufferedAmount = 8;
  149. sock._websocket.readyState = WebSocket.OPEN
  150. sock._sQ = new Uint8Array([1, 2, 3]);
  151. sock._sQlen = 3;
  152. var encoded = sock._encode_message();
  153. sock.flush();
  154. expect(sock._websocket.send).to.have.been.calledOnce;
  155. expect(sock._websocket.send).to.have.been.calledWith(encoded);
  156. });
  157. it('should return true if the websocket did not have too much buffered', function () {
  158. sock.maxBufferedAmount = 10;
  159. sock._websocket.bufferedAmount = 8;
  160. expect(sock.flush()).to.be.true;
  161. });
  162. it('should not call send if we do not have anything queued up', function () {
  163. sock._sQlen = 0;
  164. sock.maxBufferedAmount = 10;
  165. sock._websocket.bufferedAmount = 8;
  166. sock.flush();
  167. expect(sock._websocket.send).not.to.have.been.called;
  168. });
  169. it('should not send and return false if the websocket has too much buffered', function () {
  170. sock.maxBufferedAmount = 10;
  171. sock._websocket.bufferedAmount = 12;
  172. expect(sock.flush()).to.be.false;
  173. expect(sock._websocket.send).to.not.have.been.called;
  174. });
  175. });
  176. describe('send', function () {
  177. beforeEach(function () {
  178. sock.flush = sinon.spy();
  179. });
  180. it('should add to the send queue', function () {
  181. sock.send([1, 2, 3]);
  182. var sq = sock.get_sQ();
  183. expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
  184. });
  185. it('should call flush', function () {
  186. sock.send([1, 2, 3]);
  187. expect(sock.flush).to.have.been.calledOnce;
  188. });
  189. });
  190. describe('send_string', function () {
  191. beforeEach(function () {
  192. sock.send = sinon.spy();
  193. });
  194. it('should call send after converting the string to an array', function () {
  195. sock.send_string("\x01\x02\x03");
  196. expect(sock.send).to.have.been.calledWith([1, 2, 3]);
  197. });
  198. });
  199. });
  200. describe('lifecycle methods', function () {
  201. var old_WS;
  202. before(function () {
  203. old_WS = WebSocket;
  204. });
  205. var sock;
  206. beforeEach(function () {
  207. sock = new Websock();
  208. WebSocket = sinon.spy();
  209. WebSocket.OPEN = old_WS.OPEN;
  210. WebSocket.CONNECTING = old_WS.CONNECTING;
  211. WebSocket.CLOSING = old_WS.CLOSING;
  212. WebSocket.CLOSED = old_WS.CLOSED;
  213. WebSocket.prototype.binaryType = 'arraybuffer';
  214. });
  215. describe('opening', function () {
  216. it('should pick the correct protocols if none are given' , function () {
  217. });
  218. it('should open the actual websocket', function () {
  219. sock.open('ws://localhost:8675', 'binary');
  220. expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
  221. });
  222. it('should fail if we specify a protocol besides binary', function () {
  223. expect(function () { sock.open('ws:///', 'base64'); }).to.throw(Error);
  224. });
  225. // it('should initialize the event handlers')?
  226. });
  227. describe('closing', function () {
  228. beforeEach(function () {
  229. sock.open('ws://');
  230. sock._websocket.close = sinon.spy();
  231. });
  232. it('should close the actual websocket if it is open', function () {
  233. sock._websocket.readyState = WebSocket.OPEN;
  234. sock.close();
  235. expect(sock._websocket.close).to.have.been.calledOnce;
  236. });
  237. it('should close the actual websocket if it is connecting', function () {
  238. sock._websocket.readyState = WebSocket.CONNECTING;
  239. sock.close();
  240. expect(sock._websocket.close).to.have.been.calledOnce;
  241. });
  242. it('should not try to close the actual websocket if closing', function () {
  243. sock._websocket.readyState = WebSocket.CLOSING;
  244. sock.close();
  245. expect(sock._websocket.close).not.to.have.been.called;
  246. });
  247. it('should not try to close the actual websocket if closed', function () {
  248. sock._websocket.readyState = WebSocket.CLOSED;
  249. sock.close();
  250. expect(sock._websocket.close).not.to.have.been.called;
  251. });
  252. it('should reset onmessage to not call _recv_message', function () {
  253. sinon.spy(sock, '_recv_message');
  254. sock.close();
  255. sock._websocket.onmessage(null);
  256. try {
  257. expect(sock._recv_message).not.to.have.been.called;
  258. } finally {
  259. sock._recv_message.restore();
  260. }
  261. });
  262. });
  263. describe('event handlers', function () {
  264. beforeEach(function () {
  265. sock._recv_message = sinon.spy();
  266. sock.on('open', sinon.spy());
  267. sock.on('close', sinon.spy());
  268. sock.on('error', sinon.spy());
  269. sock.open('ws://');
  270. });
  271. it('should call _recv_message on a message', function () {
  272. sock._websocket.onmessage(null);
  273. expect(sock._recv_message).to.have.been.calledOnce;
  274. });
  275. it('should fail if a protocol besides binary is requested', function () {
  276. sock._websocket.protocol = 'base64';
  277. expect(sock._websocket.onopen).to.throw(Error);
  278. });
  279. it('should assume binary if no protocol was available on opening', function () {
  280. sock._websocket.protocol = null;
  281. sock._websocket.onopen();
  282. expect(sock._mode).to.equal('binary');
  283. });
  284. it('should call the open event handler on opening', function () {
  285. sock._websocket.onopen();
  286. expect(sock._eventHandlers.open).to.have.been.calledOnce;
  287. });
  288. it('should call the close event handler on closing', function () {
  289. sock._websocket.onclose();
  290. expect(sock._eventHandlers.close).to.have.been.calledOnce;
  291. });
  292. it('should call the error event handler on error', function () {
  293. sock._websocket.onerror();
  294. expect(sock._eventHandlers.error).to.have.been.calledOnce;
  295. });
  296. });
  297. after(function () {
  298. WebSocket = old_WS;
  299. });
  300. });
  301. describe('WebSocket Receiving', function () {
  302. var sock;
  303. beforeEach(function () {
  304. sock = new Websock();
  305. sock._allocate_buffers();
  306. });
  307. it('should support adding binary Uint8Array data to the receive queue', function () {
  308. var msg = { data: new Uint8Array([1, 2, 3]) };
  309. sock._mode = 'binary';
  310. sock._recv_message(msg);
  311. expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
  312. });
  313. it('should call the message event handler if present', function () {
  314. sock._eventHandlers.message = sinon.spy();
  315. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  316. sock._mode = 'binary';
  317. sock._recv_message(msg);
  318. expect(sock._eventHandlers.message).to.have.been.calledOnce;
  319. });
  320. it('should not call the message event handler if there is nothing in the receive queue', function () {
  321. sock._eventHandlers.message = sinon.spy();
  322. var msg = { data: new Uint8Array([]).buffer };
  323. sock._mode = 'binary';
  324. sock._recv_message(msg);
  325. expect(sock._eventHandlers.message).not.to.have.been.called;
  326. });
  327. it('should compact the receive queue', function () {
  328. // NB(sross): while this is an internal implementation detail, it's important to
  329. // test, otherwise the receive queue could become very large very quickly
  330. sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
  331. sock._rQlen = 6;
  332. sock.set_rQi(6);
  333. sock._rQmax = 3;
  334. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  335. sock._mode = 'binary';
  336. sock._recv_message(msg);
  337. expect(sock._rQlen).to.equal(3);
  338. expect(sock.get_rQi()).to.equal(0);
  339. });
  340. it('should automatically resize the receive queue if the incoming message is too large', function () {
  341. sock._rQ = new Uint8Array(20);
  342. sock._rQlen = 0;
  343. sock.set_rQi(0);
  344. sock._rQbufferSize = 20;
  345. sock._rQmax = 2;
  346. var msg = { data: new Uint8Array(30).buffer };
  347. sock._mode = 'binary';
  348. sock._recv_message(msg);
  349. expect(sock._rQlen).to.equal(30);
  350. expect(sock.get_rQi()).to.equal(0);
  351. expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
  352. });
  353. it('should call the error event handler on an exception', function () {
  354. sock._eventHandlers.error = sinon.spy();
  355. sock._eventHandlers.message = sinon.stub().throws();
  356. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  357. sock._mode = 'binary';
  358. sock._recv_message(msg);
  359. expect(sock._eventHandlers.error).to.have.been.calledOnce;
  360. });
  361. });
  362. describe('Data encoding', function () {
  363. before(function () { FakeWebSocket.replace(); });
  364. after(function () { FakeWebSocket.restore(); });
  365. describe('as binary data', function () {
  366. var sock;
  367. beforeEach(function () {
  368. sock = new Websock();
  369. sock.open('ws://', 'binary');
  370. sock._websocket._open();
  371. });
  372. it('should only send the send queue up to the send queue length', function () {
  373. sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
  374. sock._sQlen = 3;
  375. var res = sock._encode_message();
  376. expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
  377. });
  378. it('should properly pass the encoded data off to the actual WebSocket', function () {
  379. sock.send([1, 2, 3]);
  380. expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
  381. });
  382. });
  383. });
  384. });