Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | var expect = require('chai').expect; |
michael@0 | 2 | var util = require('./util'); |
michael@0 | 3 | |
michael@0 | 4 | var Flow = require('../lib/flow').Flow; |
michael@0 | 5 | |
michael@0 | 6 | function createFlow(log) { |
michael@0 | 7 | var flowControlId = util.random(10, 100); |
michael@0 | 8 | var flow = new Flow(flowControlId); |
michael@0 | 9 | flow._log = util.log.child(log || {}); |
michael@0 | 10 | return flow; |
michael@0 | 11 | } |
michael@0 | 12 | |
michael@0 | 13 | describe('flow.js', function() { |
michael@0 | 14 | describe('Flow class', function() { |
michael@0 | 15 | var flow; |
michael@0 | 16 | beforeEach(function() { |
michael@0 | 17 | flow = createFlow(); |
michael@0 | 18 | }); |
michael@0 | 19 | |
michael@0 | 20 | describe('._receive(frame, callback) method', function() { |
michael@0 | 21 | it('is called when there\'s a frame in the input buffer to be consumed', function(done) { |
michael@0 | 22 | var frame = { type: 'PRIORITY', flags: {}, priority: 1 }; |
michael@0 | 23 | flow._receive = function _receive(receivedFrame, callback) { |
michael@0 | 24 | expect(receivedFrame).to.equal(frame); |
michael@0 | 25 | callback(); |
michael@0 | 26 | }; |
michael@0 | 27 | flow.write(frame, done); |
michael@0 | 28 | }); |
michael@0 | 29 | it('has to be overridden by the child class, otherwise it throws', function() { |
michael@0 | 30 | expect(flow._receive.bind(flow)).to.throw(Error); |
michael@0 | 31 | }); |
michael@0 | 32 | }); |
michael@0 | 33 | describe('._send() method', function() { |
michael@0 | 34 | it('is called when the output buffer should be filled with more frames and the flow' + |
michael@0 | 35 | 'control queue is empty', function() { |
michael@0 | 36 | var sendCalled = 0; |
michael@0 | 37 | var notFlowControlledFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; |
michael@0 | 38 | flow._send = function _send() { |
michael@0 | 39 | sendCalled += 1; |
michael@0 | 40 | this.push(notFlowControlledFrame); |
michael@0 | 41 | }; |
michael@0 | 42 | expect(flow.read()).to.equal(notFlowControlledFrame); |
michael@0 | 43 | |
michael@0 | 44 | flow._window = 0; |
michael@0 | 45 | flow._queue.push({ type: 'DATA', flags: {}, data: { length: 1 } }); |
michael@0 | 46 | expect(flow.read()).to.equal(null); |
michael@0 | 47 | |
michael@0 | 48 | expect(sendCalled).to.equal(1); |
michael@0 | 49 | }); |
michael@0 | 50 | it('has to be overridden by the child class, otherwise it throws', function() { |
michael@0 | 51 | expect(flow._send.bind(flow)).to.throw(Error); |
michael@0 | 52 | }); |
michael@0 | 53 | }); |
michael@0 | 54 | describe('._increaseWindow(size) method', function() { |
michael@0 | 55 | it('should increase `this._window` by `size`', function() { |
michael@0 | 56 | flow._send = util.noop; |
michael@0 | 57 | flow._window = 0; |
michael@0 | 58 | |
michael@0 | 59 | var increase1 = util.random(0,100); |
michael@0 | 60 | var increase2 = util.random(0,100); |
michael@0 | 61 | flow._increaseWindow(increase1); |
michael@0 | 62 | flow._increaseWindow(increase2); |
michael@0 | 63 | expect(flow._window).to.equal(increase1 + increase2); |
michael@0 | 64 | |
michael@0 | 65 | flow._increaseWindow(Infinity); |
michael@0 | 66 | expect(flow._window).to.equal(Infinity); |
michael@0 | 67 | }); |
michael@0 | 68 | it('should emit error when increasing with a finite `size` when `_window` is infinite', function() { |
michael@0 | 69 | flow._send = util.noop; |
michael@0 | 70 | flow._increaseWindow(Infinity); |
michael@0 | 71 | var increase = util.random(1,100); |
michael@0 | 72 | |
michael@0 | 73 | expect(flow._increaseWindow.bind(flow, increase)).to.throw('Uncaught, unspecified "error" event.'); |
michael@0 | 74 | }); |
michael@0 | 75 | it('should emit error when `_window` grows over the window limit', function() { |
michael@0 | 76 | var WINDOW_SIZE_LIMIT = Math.pow(2, 31) - 1; |
michael@0 | 77 | flow._send = util.noop; |
michael@0 | 78 | flow._window = 0; |
michael@0 | 79 | |
michael@0 | 80 | flow._increaseWindow(WINDOW_SIZE_LIMIT); |
michael@0 | 81 | expect(flow._increaseWindow.bind(flow, 1)).to.throw('Uncaught, unspecified "error" event.'); |
michael@0 | 82 | |
michael@0 | 83 | }); |
michael@0 | 84 | }); |
michael@0 | 85 | describe('.read() method', function() { |
michael@0 | 86 | describe('when the flow control queue is not empty', function() { |
michael@0 | 87 | it('should return the first item in the queue if the window is enough', function() { |
michael@0 | 88 | var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; |
michael@0 | 89 | var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } }; |
michael@0 | 90 | flow._send = util.noop; |
michael@0 | 91 | flow._window = 10; |
michael@0 | 92 | flow._queue = [priorityFrame, dataFrame]; |
michael@0 | 93 | |
michael@0 | 94 | expect(flow.read()).to.equal(priorityFrame); |
michael@0 | 95 | expect(flow.read()).to.equal(dataFrame); |
michael@0 | 96 | }); |
michael@0 | 97 | it('should also split DATA frames when needed', function() { |
michael@0 | 98 | var buffer = new Buffer(10); |
michael@0 | 99 | var dataFrame = { type: 'DATA', flags: {}, stream: util.random(0, 100), data: buffer }; |
michael@0 | 100 | flow._send = util.noop; |
michael@0 | 101 | flow._window = 5; |
michael@0 | 102 | flow._queue = [dataFrame]; |
michael@0 | 103 | |
michael@0 | 104 | var expectedFragment = { flags: {}, type: 'DATA', stream: dataFrame.stream, data: buffer.slice(0,5) }; |
michael@0 | 105 | expect(flow.read()).to.deep.equal(expectedFragment); |
michael@0 | 106 | expect(dataFrame.data).to.deep.equal(buffer.slice(5)); |
michael@0 | 107 | }); |
michael@0 | 108 | }); |
michael@0 | 109 | }); |
michael@0 | 110 | describe('.push(frame) method', function() { |
michael@0 | 111 | it('should push `frame` into the output queue or the flow control queue', function() { |
michael@0 | 112 | var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; |
michael@0 | 113 | var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } }; |
michael@0 | 114 | flow._window = 10; |
michael@0 | 115 | |
michael@0 | 116 | flow.push(dataFrame); // output queue |
michael@0 | 117 | flow.push(dataFrame); // flow control queue, because of depleted window |
michael@0 | 118 | flow.push(priorityFrame); // flow control queue, because it's not empty |
michael@0 | 119 | |
michael@0 | 120 | expect(flow.read()).to.be.equal(dataFrame); |
michael@0 | 121 | expect(flow._queue[0]).to.be.equal(dataFrame); |
michael@0 | 122 | expect(flow._queue[1]).to.be.equal(priorityFrame); |
michael@0 | 123 | }); |
michael@0 | 124 | }); |
michael@0 | 125 | describe('.write() method', function() { |
michael@0 | 126 | it('call with a DATA frame should trigger sending WINDOW_UPDATE if remote flow control is not' + |
michael@0 | 127 | 'disabled', function(done) { |
michael@0 | 128 | flow._window = 100; |
michael@0 | 129 | flow._send = util.noop; |
michael@0 | 130 | flow._receive = function(frame, callback) { |
michael@0 | 131 | callback(); |
michael@0 | 132 | }; |
michael@0 | 133 | |
michael@0 | 134 | var buffer = new Buffer(util.random(10, 100)); |
michael@0 | 135 | flow.write({ type: 'DATA', flags: {}, data: buffer }); |
michael@0 | 136 | flow.once('readable', function() { |
michael@0 | 137 | expect(flow.read()).to.be.deep.equal({ |
michael@0 | 138 | type: 'WINDOW_UPDATE', |
michael@0 | 139 | flags: {}, |
michael@0 | 140 | stream: flow._flowControlId, |
michael@0 | 141 | window_size: buffer.length |
michael@0 | 142 | }); |
michael@0 | 143 | done(); |
michael@0 | 144 | }); |
michael@0 | 145 | }); |
michael@0 | 146 | }); |
michael@0 | 147 | }); |
michael@0 | 148 | describe('test scenario', function() { |
michael@0 | 149 | var flow1, flow2; |
michael@0 | 150 | beforeEach(function() { |
michael@0 | 151 | flow1 = createFlow({ flow: 1 }); |
michael@0 | 152 | flow2 = createFlow({ flow: 2 }); |
michael@0 | 153 | flow1._flowControlId = flow2._flowControlId; |
michael@0 | 154 | flow1._send = flow2._send = util.noop; |
michael@0 | 155 | flow1._receive = flow2._receive = function(frame, callback) { callback(); }; |
michael@0 | 156 | }); |
michael@0 | 157 | |
michael@0 | 158 | describe('sending a large data stream', function() { |
michael@0 | 159 | it('should work as expected', function(done) { |
michael@0 | 160 | // Sender side |
michael@0 | 161 | var frameNumber = util.random(5, 8); |
michael@0 | 162 | var input = []; |
michael@0 | 163 | flow1._send = function _send() { |
michael@0 | 164 | if (input.length >= frameNumber) { |
michael@0 | 165 | this.push({ type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(0) }); |
michael@0 | 166 | this.push(null); |
michael@0 | 167 | } else { |
michael@0 | 168 | var buffer = new Buffer(util.random(1000, 100000)); |
michael@0 | 169 | input.push(buffer); |
michael@0 | 170 | this.push({ type: 'DATA', flags: {}, data: buffer }); |
michael@0 | 171 | } |
michael@0 | 172 | }; |
michael@0 | 173 | |
michael@0 | 174 | // Receiver side |
michael@0 | 175 | var output = []; |
michael@0 | 176 | flow2._receive = function _receive(frame, callback) { |
michael@0 | 177 | if (frame.type === 'DATA') { |
michael@0 | 178 | output.push(frame.data); |
michael@0 | 179 | } |
michael@0 | 180 | if (frame.flags.END_STREAM) { |
michael@0 | 181 | this.emit('end_stream'); |
michael@0 | 182 | } |
michael@0 | 183 | callback(); |
michael@0 | 184 | }; |
michael@0 | 185 | |
michael@0 | 186 | // Checking results |
michael@0 | 187 | flow2.on('end_stream', function() { |
michael@0 | 188 | input = util.concat(input); |
michael@0 | 189 | output = util.concat(output); |
michael@0 | 190 | |
michael@0 | 191 | expect(input).to.deep.equal(output); |
michael@0 | 192 | |
michael@0 | 193 | done(); |
michael@0 | 194 | }); |
michael@0 | 195 | |
michael@0 | 196 | // Start piping |
michael@0 | 197 | flow1.pipe(flow2).pipe(flow1); |
michael@0 | 198 | }); |
michael@0 | 199 | }); |
michael@0 | 200 | }); |
michael@0 | 201 | }); |