blob: 75011ef0c6e22208ca8c10056113029cfe41baf7 [file] [log] [blame]
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.net.ChannelRequestTest');
goog.setTestOnly('goog.net.ChannelRequestTest');
goog.require('goog.Uri');
goog.require('goog.functions');
goog.require('goog.net.BrowserChannel');
goog.require('goog.net.ChannelDebug');
goog.require('goog.net.ChannelRequest');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.net.XhrIo');
goog.require('goog.testing.recordFunction');
var channelRequest;
var mockBrowserChannel;
var mockClock;
var stubs;
var xhrIo;
/**
* Time to wait for a network request to time out, before aborting.
*/
var WATCHDOG_TIME = 2000;
/**
* Time to throttle readystatechange events.
*/
var THROTTLE_TIME = 500;
/**
* A really long time - used to make sure no more timeouts will fire.
*/
var ALL_DAY_MS = 1000 * 60 * 60 * 24;
function setUp() {
mockClock = new goog.testing.MockClock();
mockClock.install();
stubs = new goog.testing.PropertyReplacer();
}
function tearDown() {
stubs.reset();
mockClock.uninstall();
}
/**
* Constructs a duck-type BrowserChannel that tracks the completed requests.
* @constructor
*/
function MockBrowserChannel() {
this.reachabilityEvents = {};
this.isClosed = function() {
return false;
};
this.isActive = function() {
return true;
};
this.shouldUseSecondaryDomains = function() {
return false;
};
this.completedRequests = [];
this.notifyServerReachabilityEvent = function(reachabilityType) {
if (!this.reachabilityEvents[reachabilityType]) {
this.reachabilityEvents[reachabilityType] = 0;
}
this.reachabilityEvents[reachabilityType]++;
};
this.onRequestComplete = function(request) {
this.completedRequests.push(request);
};
this.onRequestData = function(request, data) {};
}
/**
* Creates a real ChannelRequest object, with some modifications for
* testability:
* <ul>
* <li>The BrowserChannel is a MockBrowserChannel.
* <li>The new watchdogTimeoutCallCount property tracks onWatchDogTimeout_()
* calls.
* <li>The timeout is set to WATCHDOG_TIME.
* </ul>
*/
function createChannelRequest() {
xhrIo = new goog.testing.net.XhrIo();
xhrIo.abort = xhrIo.abort || function() {
this.active_ = false;
};
// Install mock browser channel and no-op debug logger.
mockBrowserChannel = new MockBrowserChannel();
channelRequest = new goog.net.ChannelRequest(
mockBrowserChannel,
new goog.net.ChannelDebug());
// Install test XhrIo.
mockBrowserChannel.createXhrIo = function() {
return xhrIo;
};
// Install watchdogTimeoutCallCount.
channelRequest.watchdogTimeoutCallCount = 0;
channelRequest.originalOnWatchDogTimeout = channelRequest.onWatchDogTimeout_;
channelRequest.onWatchDogTimeout_ = function() {
this.watchdogTimeoutCallCount++;
return this.originalOnWatchDogTimeout();
};
channelRequest.setTimeout(WATCHDOG_TIME);
}
/**
* Run through the lifecycle of a long lived request, checking that the right
* network events are reported.
*/
function testNetworkEvents() {
createChannelRequest();
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
checkReachabilityEvents(1, 0, 0, 0);
if (goog.net.ChannelRequest.supportsXhrStreaming()) {
xhrIo.simulatePartialResponse('17\nI am a BC Message');
checkReachabilityEvents(1, 0, 0, 1);
xhrIo.simulatePartialResponse('23\nI am another BC Message');
checkReachabilityEvents(1, 0, 0, 2);
xhrIo.simulateResponse(200, '16\Final BC Message');
checkReachabilityEvents(1, 1, 0, 2);
} else {
xhrIo.simulateResponse(200, '16\Final BC Message');
checkReachabilityEvents(1, 1, 0, 0);
}
}
/**
* Test throttling of readystatechange events.
*/
function testNetworkEvents_throttleReadyStateChange() {
createChannelRequest();
channelRequest.setReadyStateChangeThrottle(THROTTLE_TIME);
var recordedHandler =
goog.testing.recordFunction(channelRequest.xmlHttpHandler_);
stubs.set(channelRequest, 'xmlHttpHandler_', recordedHandler);
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
assertEquals(1, recordedHandler.getCallCount());
checkReachabilityEvents(1, 0, 0, 0);
if (goog.net.ChannelRequest.supportsXhrStreaming()) {
xhrIo.simulatePartialResponse('17\nI am a BC Message');
checkReachabilityEvents(1, 0, 0, 1);
assertEquals(3, recordedHandler.getCallCount());
// Second event should be throttled
xhrIo.simulatePartialResponse('23\nI am another BC Message');
assertEquals(3, recordedHandler.getCallCount());
xhrIo.simulatePartialResponse('27\nI am yet another BC Message');
assertEquals(3, recordedHandler.getCallCount());
mockClock.tick(THROTTLE_TIME);
checkReachabilityEvents(1, 0, 0, 3);
// Only one more call because of throttling.
assertEquals(4, recordedHandler.getCallCount());
xhrIo.simulateResponse(200, '16\Final BC Message');
checkReachabilityEvents(1, 1, 0, 3);
assertEquals(5, recordedHandler.getCallCount());
} else {
xhrIo.simulateResponse(200, '16\Final BC Message');
checkReachabilityEvents(1, 1, 0, 0);
}
}
/**
* Make sure that the request "completes" with an error when the timeout
* expires.
*/
function testRequestTimeout() {
createChannelRequest();
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
assertEquals(0, channelRequest.watchdogTimeoutCallCount);
assertEquals(0, channelRequest.channel_.completedRequests.length);
// Watchdog timeout.
mockClock.tick(WATCHDOG_TIME);
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
assertEquals(1, channelRequest.channel_.completedRequests.length);
assertFalse(channelRequest.getSuccess());
// Make sure no more timers are firing.
mockClock.tick(ALL_DAY_MS);
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
assertEquals(1, channelRequest.channel_.completedRequests.length);
checkReachabilityEvents(1, 0, 1, 0);
}
function testRequestTimeoutWithUnexpectedException() {
createChannelRequest();
channelRequest.channel_.createXhrIo = goog.functions.error('Weird error');
try {
channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null);
fail('Expected error');
} catch (e) {
assertEquals('Weird error', e.message);
}
assertEquals(0, channelRequest.watchdogTimeoutCallCount);
assertEquals(0, channelRequest.channel_.completedRequests.length);
// Watchdog timeout.
mockClock.tick(WATCHDOG_TIME);
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
assertEquals(1, channelRequest.channel_.completedRequests.length);
assertFalse(channelRequest.getSuccess());
// Make sure no more timers are firing.
mockClock.tick(ALL_DAY_MS);
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
assertEquals(1, channelRequest.channel_.completedRequests.length);
checkReachabilityEvents(0, 0, 1, 0);
}
function testActiveXBlocked() {
createChannelRequest();
stubs.set(goog.global, 'ActiveXObject',
goog.functions.error('Active X blocked'));
channelRequest.tridentGet(new goog.Uri('some_uri'), false);
assertFalse(channelRequest.getSuccess());
assertEquals(goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED,
channelRequest.getLastError());
checkReachabilityEvents(0, 0, 0, 0);
}
function checkReachabilityEvents(reqMade, reqSucceeded, reqFail, backChannel) {
var Reachability = goog.net.BrowserChannel.ServerReachability;
assertEquals(reqMade,
mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_MADE] || 0);
assertEquals(reqSucceeded,
mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_SUCCEEDED] ||
0);
assertEquals(reqFail,
mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_FAILED] || 0);
assertEquals(backChannel,
mockBrowserChannel.reachabilityEvents[
Reachability.BACK_CHANNEL_ACTIVITY] ||
0);
}