test.websock.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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._sQ = new Uint8Array([1, 2, 3]);
  150. sock._sQlen = 3;
  151. var encoded = sock._encode_message();
  152. sock.flush();
  153. expect(sock._websocket.send).to.have.been.calledOnce;
  154. expect(sock._websocket.send).to.have.been.calledWith(encoded);
  155. });
  156. it('should return true if the websocket did not have too much buffered', function () {
  157. sock.maxBufferedAmount = 10;
  158. sock._websocket.bufferedAmount = 8;
  159. expect(sock.flush()).to.be.true;
  160. });
  161. it('should not call send if we do not have anything queued up', function () {
  162. sock._sQlen = 0;
  163. sock.maxBufferedAmount = 10;
  164. sock._websocket.bufferedAmount = 8;
  165. sock.flush();
  166. expect(sock._websocket.send).not.to.have.been.called;
  167. });
  168. it('should not send and return false if the websocket has too much buffered', function () {
  169. sock.maxBufferedAmount = 10;
  170. sock._websocket.bufferedAmount = 12;
  171. expect(sock.flush()).to.be.false;
  172. expect(sock._websocket.send).to.not.have.been.called;
  173. });
  174. });
  175. describe('send', function () {
  176. beforeEach(function () {
  177. sock.flush = sinon.spy();
  178. });
  179. it('should add to the send queue', function () {
  180. sock.send([1, 2, 3]);
  181. var sq = sock.get_sQ();
  182. expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
  183. });
  184. it('should call flush', function () {
  185. sock.send([1, 2, 3]);
  186. expect(sock.flush).to.have.been.calledOnce;
  187. });
  188. });
  189. describe('send_string', function () {
  190. beforeEach(function () {
  191. sock.send = sinon.spy();
  192. });
  193. it('should call send after converting the string to an array', function () {
  194. sock.send_string("\x01\x02\x03");
  195. expect(sock.send).to.have.been.calledWith([1, 2, 3]);
  196. });
  197. });
  198. });
  199. describe('lifecycle methods', function () {
  200. var old_WS;
  201. before(function () {
  202. old_WS = WebSocket;
  203. });
  204. var sock;
  205. beforeEach(function () {
  206. sock = new Websock();
  207. WebSocket = sinon.spy();
  208. WebSocket.OPEN = old_WS.OPEN;
  209. WebSocket.CONNECTING = old_WS.CONNECTING;
  210. WebSocket.CLOSING = old_WS.CLOSING;
  211. WebSocket.CLOSED = old_WS.CLOSED;
  212. WebSocket.prototype.binaryType = 'arraybuffer';
  213. });
  214. describe('opening', function () {
  215. it('should pick the correct protocols if none are given' , function () {
  216. });
  217. it('should open the actual websocket', function () {
  218. sock.open('ws://localhost:8675', 'binary');
  219. expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
  220. });
  221. it('should fail if we specify a protocol besides binary', function () {
  222. expect(function () { sock.open('ws:///', 'base64'); }).to.throw(Error);
  223. });
  224. // it('should initialize the event handlers')?
  225. });
  226. describe('closing', function () {
  227. beforeEach(function () {
  228. sock.open('ws://');
  229. sock._websocket.close = sinon.spy();
  230. });
  231. it('should close the actual websocket if it is open', function () {
  232. sock._websocket.readyState = WebSocket.OPEN;
  233. sock.close();
  234. expect(sock._websocket.close).to.have.been.calledOnce;
  235. });
  236. it('should close the actual websocket if it is connecting', function () {
  237. sock._websocket.readyState = WebSocket.CONNECTING;
  238. sock.close();
  239. expect(sock._websocket.close).to.have.been.calledOnce;
  240. });
  241. it('should not try to close the actual websocket if closing', function () {
  242. sock._websocket.readyState = WebSocket.CLOSING;
  243. sock.close();
  244. expect(sock._websocket.close).not.to.have.been.called;
  245. });
  246. it('should not try to close the actual websocket if closed', function () {
  247. sock._websocket.readyState = WebSocket.CLOSED;
  248. sock.close();
  249. expect(sock._websocket.close).not.to.have.been.called;
  250. });
  251. it('should reset onmessage to not call _recv_message', function () {
  252. sinon.spy(sock, '_recv_message');
  253. sock.close();
  254. sock._websocket.onmessage(null);
  255. try {
  256. expect(sock._recv_message).not.to.have.been.called;
  257. } finally {
  258. sock._recv_message.restore();
  259. }
  260. });
  261. });
  262. describe('event handlers', function () {
  263. beforeEach(function () {
  264. sock._recv_message = sinon.spy();
  265. sock.on('open', sinon.spy());
  266. sock.on('close', sinon.spy());
  267. sock.on('error', sinon.spy());
  268. sock.open('ws://');
  269. });
  270. it('should call _recv_message on a message', function () {
  271. sock._websocket.onmessage(null);
  272. expect(sock._recv_message).to.have.been.calledOnce;
  273. });
  274. it('should fail if a protocol besides binary is requested', function () {
  275. sock._websocket.protocol = 'base64';
  276. expect(sock._websocket.onopen).to.throw(Error);
  277. });
  278. it('should assume binary if no protocol was available on opening', function () {
  279. sock._websocket.protocol = null;
  280. sock._websocket.onopen();
  281. expect(sock._mode).to.equal('binary');
  282. });
  283. it('should call the open event handler on opening', function () {
  284. sock._websocket.onopen();
  285. expect(sock._eventHandlers.open).to.have.been.calledOnce;
  286. });
  287. it('should call the close event handler on closing', function () {
  288. sock._websocket.onclose();
  289. expect(sock._eventHandlers.close).to.have.been.calledOnce;
  290. });
  291. it('should call the error event handler on error', function () {
  292. sock._websocket.onerror();
  293. expect(sock._eventHandlers.error).to.have.been.calledOnce;
  294. });
  295. });
  296. after(function () {
  297. WebSocket = old_WS;
  298. });
  299. });
  300. describe('WebSocket Receiving', function () {
  301. var sock;
  302. beforeEach(function () {
  303. sock = new Websock();
  304. sock._allocate_buffers();
  305. });
  306. it('should support adding binary Uint8Array data to the receive queue', function () {
  307. var msg = { data: new Uint8Array([1, 2, 3]) };
  308. sock._mode = 'binary';
  309. sock._recv_message(msg);
  310. expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
  311. });
  312. it('should call the message event handler if present', function () {
  313. sock._eventHandlers.message = sinon.spy();
  314. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  315. sock._mode = 'binary';
  316. sock._recv_message(msg);
  317. expect(sock._eventHandlers.message).to.have.been.calledOnce;
  318. });
  319. it('should not call the message event handler if there is nothing in the receive queue', function () {
  320. sock._eventHandlers.message = sinon.spy();
  321. var msg = { data: new Uint8Array([]).buffer };
  322. sock._mode = 'binary';
  323. sock._recv_message(msg);
  324. expect(sock._eventHandlers.message).not.to.have.been.called;
  325. });
  326. it('should compact the receive queue', function () {
  327. // NB(sross): while this is an internal implementation detail, it's important to
  328. // test, otherwise the receive queue could become very large very quickly
  329. sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
  330. sock._rQlen = 6;
  331. sock.set_rQi(6);
  332. sock._rQmax = 3;
  333. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  334. sock._mode = 'binary';
  335. sock._recv_message(msg);
  336. expect(sock._rQlen).to.equal(3);
  337. expect(sock.get_rQi()).to.equal(0);
  338. });
  339. it('should call the error event handler on an exception', function () {
  340. sock._eventHandlers.error = sinon.spy();
  341. sock._eventHandlers.message = sinon.stub().throws();
  342. var msg = { data: new Uint8Array([1, 2, 3]).buffer };
  343. sock._mode = 'binary';
  344. sock._recv_message(msg);
  345. expect(sock._eventHandlers.error).to.have.been.calledOnce;
  346. });
  347. });
  348. describe('Data encoding', function () {
  349. before(function () { FakeWebSocket.replace(); });
  350. after(function () { FakeWebSocket.restore(); });
  351. describe('as binary data', function () {
  352. var sock;
  353. beforeEach(function () {
  354. sock = new Websock();
  355. sock.open('ws://', 'binary');
  356. sock._websocket._open();
  357. });
  358. it('should only send the send queue up to the send queue length', function () {
  359. sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
  360. sock._sQlen = 3;
  361. var res = sock._encode_message();
  362. expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
  363. });
  364. it('should properly pass the encoded data off to the actual WebSocket', function () {
  365. sock.send([1, 2, 3]);
  366. expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
  367. });
  368. });
  369. });
  370. });