1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/xpcshell/node-http2/node_modules/http2-protocol/test/flow.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,201 @@ 1.4 +var expect = require('chai').expect; 1.5 +var util = require('./util'); 1.6 + 1.7 +var Flow = require('../lib/flow').Flow; 1.8 + 1.9 +function createFlow(log) { 1.10 + var flowControlId = util.random(10, 100); 1.11 + var flow = new Flow(flowControlId); 1.12 + flow._log = util.log.child(log || {}); 1.13 + return flow; 1.14 +} 1.15 + 1.16 +describe('flow.js', function() { 1.17 + describe('Flow class', function() { 1.18 + var flow; 1.19 + beforeEach(function() { 1.20 + flow = createFlow(); 1.21 + }); 1.22 + 1.23 + describe('._receive(frame, callback) method', function() { 1.24 + it('is called when there\'s a frame in the input buffer to be consumed', function(done) { 1.25 + var frame = { type: 'PRIORITY', flags: {}, priority: 1 }; 1.26 + flow._receive = function _receive(receivedFrame, callback) { 1.27 + expect(receivedFrame).to.equal(frame); 1.28 + callback(); 1.29 + }; 1.30 + flow.write(frame, done); 1.31 + }); 1.32 + it('has to be overridden by the child class, otherwise it throws', function() { 1.33 + expect(flow._receive.bind(flow)).to.throw(Error); 1.34 + }); 1.35 + }); 1.36 + describe('._send() method', function() { 1.37 + it('is called when the output buffer should be filled with more frames and the flow' + 1.38 + 'control queue is empty', function() { 1.39 + var sendCalled = 0; 1.40 + var notFlowControlledFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; 1.41 + flow._send = function _send() { 1.42 + sendCalled += 1; 1.43 + this.push(notFlowControlledFrame); 1.44 + }; 1.45 + expect(flow.read()).to.equal(notFlowControlledFrame); 1.46 + 1.47 + flow._window = 0; 1.48 + flow._queue.push({ type: 'DATA', flags: {}, data: { length: 1 } }); 1.49 + expect(flow.read()).to.equal(null); 1.50 + 1.51 + expect(sendCalled).to.equal(1); 1.52 + }); 1.53 + it('has to be overridden by the child class, otherwise it throws', function() { 1.54 + expect(flow._send.bind(flow)).to.throw(Error); 1.55 + }); 1.56 + }); 1.57 + describe('._increaseWindow(size) method', function() { 1.58 + it('should increase `this._window` by `size`', function() { 1.59 + flow._send = util.noop; 1.60 + flow._window = 0; 1.61 + 1.62 + var increase1 = util.random(0,100); 1.63 + var increase2 = util.random(0,100); 1.64 + flow._increaseWindow(increase1); 1.65 + flow._increaseWindow(increase2); 1.66 + expect(flow._window).to.equal(increase1 + increase2); 1.67 + 1.68 + flow._increaseWindow(Infinity); 1.69 + expect(flow._window).to.equal(Infinity); 1.70 + }); 1.71 + it('should emit error when increasing with a finite `size` when `_window` is infinite', function() { 1.72 + flow._send = util.noop; 1.73 + flow._increaseWindow(Infinity); 1.74 + var increase = util.random(1,100); 1.75 + 1.76 + expect(flow._increaseWindow.bind(flow, increase)).to.throw('Uncaught, unspecified "error" event.'); 1.77 + }); 1.78 + it('should emit error when `_window` grows over the window limit', function() { 1.79 + var WINDOW_SIZE_LIMIT = Math.pow(2, 31) - 1; 1.80 + flow._send = util.noop; 1.81 + flow._window = 0; 1.82 + 1.83 + flow._increaseWindow(WINDOW_SIZE_LIMIT); 1.84 + expect(flow._increaseWindow.bind(flow, 1)).to.throw('Uncaught, unspecified "error" event.'); 1.85 + 1.86 + }); 1.87 + }); 1.88 + describe('.read() method', function() { 1.89 + describe('when the flow control queue is not empty', function() { 1.90 + it('should return the first item in the queue if the window is enough', function() { 1.91 + var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; 1.92 + var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } }; 1.93 + flow._send = util.noop; 1.94 + flow._window = 10; 1.95 + flow._queue = [priorityFrame, dataFrame]; 1.96 + 1.97 + expect(flow.read()).to.equal(priorityFrame); 1.98 + expect(flow.read()).to.equal(dataFrame); 1.99 + }); 1.100 + it('should also split DATA frames when needed', function() { 1.101 + var buffer = new Buffer(10); 1.102 + var dataFrame = { type: 'DATA', flags: {}, stream: util.random(0, 100), data: buffer }; 1.103 + flow._send = util.noop; 1.104 + flow._window = 5; 1.105 + flow._queue = [dataFrame]; 1.106 + 1.107 + var expectedFragment = { flags: {}, type: 'DATA', stream: dataFrame.stream, data: buffer.slice(0,5) }; 1.108 + expect(flow.read()).to.deep.equal(expectedFragment); 1.109 + expect(dataFrame.data).to.deep.equal(buffer.slice(5)); 1.110 + }); 1.111 + }); 1.112 + }); 1.113 + describe('.push(frame) method', function() { 1.114 + it('should push `frame` into the output queue or the flow control queue', function() { 1.115 + var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 }; 1.116 + var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } }; 1.117 + flow._window = 10; 1.118 + 1.119 + flow.push(dataFrame); // output queue 1.120 + flow.push(dataFrame); // flow control queue, because of depleted window 1.121 + flow.push(priorityFrame); // flow control queue, because it's not empty 1.122 + 1.123 + expect(flow.read()).to.be.equal(dataFrame); 1.124 + expect(flow._queue[0]).to.be.equal(dataFrame); 1.125 + expect(flow._queue[1]).to.be.equal(priorityFrame); 1.126 + }); 1.127 + }); 1.128 + describe('.write() method', function() { 1.129 + it('call with a DATA frame should trigger sending WINDOW_UPDATE if remote flow control is not' + 1.130 + 'disabled', function(done) { 1.131 + flow._window = 100; 1.132 + flow._send = util.noop; 1.133 + flow._receive = function(frame, callback) { 1.134 + callback(); 1.135 + }; 1.136 + 1.137 + var buffer = new Buffer(util.random(10, 100)); 1.138 + flow.write({ type: 'DATA', flags: {}, data: buffer }); 1.139 + flow.once('readable', function() { 1.140 + expect(flow.read()).to.be.deep.equal({ 1.141 + type: 'WINDOW_UPDATE', 1.142 + flags: {}, 1.143 + stream: flow._flowControlId, 1.144 + window_size: buffer.length 1.145 + }); 1.146 + done(); 1.147 + }); 1.148 + }); 1.149 + }); 1.150 + }); 1.151 + describe('test scenario', function() { 1.152 + var flow1, flow2; 1.153 + beforeEach(function() { 1.154 + flow1 = createFlow({ flow: 1 }); 1.155 + flow2 = createFlow({ flow: 2 }); 1.156 + flow1._flowControlId = flow2._flowControlId; 1.157 + flow1._send = flow2._send = util.noop; 1.158 + flow1._receive = flow2._receive = function(frame, callback) { callback(); }; 1.159 + }); 1.160 + 1.161 + describe('sending a large data stream', function() { 1.162 + it('should work as expected', function(done) { 1.163 + // Sender side 1.164 + var frameNumber = util.random(5, 8); 1.165 + var input = []; 1.166 + flow1._send = function _send() { 1.167 + if (input.length >= frameNumber) { 1.168 + this.push({ type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(0) }); 1.169 + this.push(null); 1.170 + } else { 1.171 + var buffer = new Buffer(util.random(1000, 100000)); 1.172 + input.push(buffer); 1.173 + this.push({ type: 'DATA', flags: {}, data: buffer }); 1.174 + } 1.175 + }; 1.176 + 1.177 + // Receiver side 1.178 + var output = []; 1.179 + flow2._receive = function _receive(frame, callback) { 1.180 + if (frame.type === 'DATA') { 1.181 + output.push(frame.data); 1.182 + } 1.183 + if (frame.flags.END_STREAM) { 1.184 + this.emit('end_stream'); 1.185 + } 1.186 + callback(); 1.187 + }; 1.188 + 1.189 + // Checking results 1.190 + flow2.on('end_stream', function() { 1.191 + input = util.concat(input); 1.192 + output = util.concat(output); 1.193 + 1.194 + expect(input).to.deep.equal(output); 1.195 + 1.196 + done(); 1.197 + }); 1.198 + 1.199 + // Start piping 1.200 + flow1.pipe(flow2).pipe(flow1); 1.201 + }); 1.202 + }); 1.203 + }); 1.204 +});