/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* eslint-disable mozilla/use-chromeutils-generateqi */ const { NetworkThrottleManager } = ChromeUtils.importESModule( "resource://devtools/shared/network-observer/NetworkThrottleManager.sys.mjs" ); const nsIScriptableInputStream = Ci.nsIScriptableInputStream; function TestStreamListener() { this.state = "initial"; } TestStreamListener.prototype = { onStartRequest() { this.setState("start"); }, onStopRequest() { this.setState("stop"); }, onDataAvailable(request, inputStream, offset, count) { const sin = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( nsIScriptableInputStream ); sin.init(inputStream); this.data = sin.read(count); this.setState("data"); }, setState(state) { this.state = state; if (this._deferred) { this._deferred.resolve(state); this._deferred = null; } }, onStateChanged() { if (!this._deferred) { let resolve, reject; const promise = new Promise(function (res, rej) { resolve = res; reject = rej; }); this._deferred = { resolve, reject, promise }; } return this._deferred.promise; }, }; function TestChannel() { this.state = "initial"; this.testListener = new TestStreamListener(); this._throttleQueue = null; } TestChannel.prototype = { QueryInterface() { return this; }, get throttleQueue() { return this._throttleQueue; }, set throttleQueue(q) { this._throttleQueue = q; this.state = "throttled"; }, setNewListener(listener) { this.listener = listener; this.state = "listener"; return this.testListener; }, }; add_task(async function () { const throttler = new NetworkThrottleManager({ latencyMean: 1, latencyMax: 1, downloadBPSMean: 500, downloadBPSMax: 500, uploadBPSMean: 500, uploadBPSMax: 500, }); const uploadChannel = new TestChannel(); throttler.manageUpload(uploadChannel); equal( uploadChannel.state, "throttled", "NetworkThrottleManager set throttleQueue" ); const downloadChannel = new TestChannel(); const testListener = downloadChannel.testListener; const listener = throttler.manage(downloadChannel); equal( downloadChannel.state, "listener", "NetworkThrottleManager called setNewListener" ); equal(testListener.state, "initial", "test listener in initial state"); // This method must be passed through immediately. listener.onStartRequest(null); equal(testListener.state, "start", "test listener started"); const TEST_INPUT = "hi bob"; const testStream = Cc["@mozilla.org/storagestream;1"].createInstance( Ci.nsIStorageStream ); testStream.init(512, 512); const out = testStream.getOutputStream(0); out.write(TEST_INPUT, TEST_INPUT.length); out.close(); const testInputStream = testStream.newInputStream(0); const activityDistributor = Cc[ "@mozilla.org/network/http-activity-distributor;1" ].getService(Ci.nsIHttpActivityDistributor); let activitySeen = false; listener.addActivityCallback( () => { activitySeen = true; }, null, null, null, activityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE, null, TEST_INPUT.length, null ); // onDataAvailable is required to immediately read the data. listener.onDataAvailable(null, testInputStream, 0, 6); equal(testInputStream.available(), 0, "no more data should be available"); equal( testListener.state, "start", "test listener should not have received data" ); equal(activitySeen, false, "activity not distributed yet"); let newState = await testListener.onStateChanged(); equal(newState, "data", "test listener received data"); equal(testListener.data, TEST_INPUT, "test listener received all the data"); equal(activitySeen, true, "activity has been distributed"); const onChange = testListener.onStateChanged(); listener.onStopRequest(null, null); newState = await onChange; equal(newState, "stop", "onStateChanged reported"); });