Split ExtensionWebRequestApiTest.WebRequestEvents into multiple tests to

fix flakiness.

BUG=91715
TEST=no

Review URL: http://codereview.chromium.org/7607003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96075 0039d316-1c4b-4281-b951-d872f2087c98
parent 7508415e
......@@ -50,25 +50,36 @@ class ExtensionWebRequestApiTest : public ExtensionApiTest {
}
};
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequest) {
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestApi) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
ASSERT_TRUE(RunExtensionTest("webrequest/api")) << message_;
ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_api.html")) << message_;
}
// http://crbug.com/91715
#if defined(OS_MACOSX)
#define MAYBE_WebRequestEvents DISABLED_WebRequestEvents
#else
#define MAYBE_WebRequestEvents WebRequestEvents
#endif
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestSimple) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_WebRequestEvents) {
ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_simple.html")) <<
message_;
}
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestComplex) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
// Needed for the auth tests.
CancelLoginDialog login_dialog_helper;
ASSERT_TRUE(RunExtensionTest("webrequest/events")) << message_;
ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_complex.html")) <<
message_;
}
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestBlocking) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_blocking.html")) <<
message_;
}
{
"name": "api",
"version": "1.0",
"description": "Tests that the webRequest API exists.",
"permissions": ["experimental"],
"background_page": "test.html"
}
{
"name": "request",
"version": "1.0",
"description": "Tests the webRequest API events.",
"permissions": ["experimental", "tabs", "http://*/*"],
"background_page": "test.html"
}
var getURL = chrome.extension.getURL;
var deepEq = chrome.test.checkDeepEq;
var expectedEventData;
var capturedEventData;
var expectedEventOrder;
var tabId;
var testServerPort;
var eventsCaptured;
function runTests(tests) {
chrome.tabs.create({url: "about:blank"}, function(tab) {
tabId = tab.id;
chrome.test.getConfig(function(config) {
testServerPort = config.testServer.port;
chrome.test.runTests(tests);
});
});
}
// Returns an URL from the test server, fixing up the port. Must be called
// from within a test case passed to runTests.
function getServerURL(path) {
if (!testServerPort)
throw new Error("Called getServerURL outside of runTests.");
return "http://www.a.com:" + testServerPort + "/" + path;
}
// Helper to advance to the next test only when the tab has finished loading.
// This is because tabs.update can sometimes fail if the tab is in the middle
// of a navigation (from the previous test), resulting in flakiness.
function navigateAndWait(url, callback) {
var done = chrome.test.listenForever(chrome.tabs.onUpdated,
function (_, info, tab) {
if (tab.id == tabId && info.status == "complete") {
if (callback) callback();
done();
}
});
chrome.tabs.update(tabId, {url: url});
}
// data: array of extected events, each one is a dictionary:
// { label: "<unique identifier>",
// event: "<webrequest event type>",
// details: { <expected details of the webrequest event> },
// retval: { <dictionary that the event handler shall return> } (optional)
// }
// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
// event with label "a" needs to occur before event with label "b". The
// relative order of "a" and "d" does not matter.
// filter: filter dictionary passed on to the event subscription of the
// webRequest API.
// extraInfoSpec: the union of all desired extraInfoSpecs for the events.
function expect(data, order, filter, extraInfoSpec) {
expectedEventData = data;
capturedEventData = [];
expectedEventOrder = order;
eventsCaptured = chrome.test.callbackAdded();
tabAndFrameUrls = {}; // Maps "{tabId}-{frameId}" to the URL of the frame.
removeListeners();
initListeners(filter || {}, extraInfoSpec || []);
}
function checkExpectations() {
if (capturedEventData.length < expectedEventData.length) {
return;
}
if (capturedEventData.length > expectedEventData.length) {
chrome.test.fail("Recorded too many events. " +
JSON.stringify(capturedEventData));
return;
}
// We have ensured that capturedEventData contains exactly the same elements
// as expectedEventData. Now we need to verify the ordering.
// Step 1: build positions such that
// positions[<event-label>]=<position of this event in capturedEventData>
var curPos = 0;
var positions = {}
capturedEventData.forEach(function (event) {
chrome.test.assertTrue(event.hasOwnProperty("label"));
positions[event.label] = curPos;
curPos++;
});
// Step 2: check that elements arrived in correct order
expectedEventOrder.forEach(function (order) {
var previousLabel = undefined;
order.forEach(function(label) {
if (previousLabel === undefined) {
previousLabel = label;
return;
}
chrome.test.assertTrue(positions[previousLabel] < positions[label],
"Event " + previousLabel + " is supposed to arrive before " +
label + ".");
previousLabel = label;
});
});
eventsCaptured();
}
// Simple check to see that we have a User-Agent header, and that it contains
// an expected value. This is a basic check that the request headers are valid.
function checkUserAgent(headers) {
for (var i in headers) {
if (headers[i].name.toLowerCase() == "user-agent")
return headers[i].value.toLowerCase().indexOf("chrome") != -1;
}
return false;
}
function captureEvent(name, details) {
// Ignore system-level requests like safebrowsing updates and favicon fetches
// since they are unpredictable.
if (details.tabId == -1 || details.type == "other" ||
details.url.match(/\/favicon.ico$/) ||
details.url.match(/https:\/\/dl.google.com/))
return;
// Pull the extra per-event options out of the expected data. These let
// us specify special return values per event.
var currentIndex = capturedEventData.length;
var extraOptions;
if (expectedEventData.length > currentIndex) {
retval = expectedEventData[currentIndex].retval;
}
// Check that the frameId can be used to reliably determine the URL of the
// frame that caused requests.
if (name == "onBeforeRequest") {
chrome.test.assertTrue('frameId' in details &&
typeof details.frameId === 'number');
chrome.test.assertTrue('tabId' in details &&
typeof details.tabId === 'number');
var key = details.tabId + "-" + details.frameId;
if (details.type == "main_frame" || details.type == "sub_frame") {
tabAndFrameUrls[key] = details.url;
}
details.frameUrl = tabAndFrameUrls[key] || "unknown frame URL";
}
delete details.frameId;
delete details.requestId;
delete details.timeStamp;
if (details.requestHeaders) {
details.requestHeadersValid = checkUserAgent(details.requestHeaders);
delete details.requestHeaders;
}
if (details.responseHeaders) {
details.responseHeadersExist = true;
delete details.responseHeaders;
}
// find |details| in expectedEventData
var found = false;
var label = undefined;
expectedEventData.forEach(function (exp) {
if (deepEq(exp.event, name) && deepEq(exp.details, details)) {
if (found) {
chrome.test.fail("Received event twice '" + name + "':" +
JSON.stringify(details));
} else {
found = true;
label = exp.label;
}
}
});
if (!found) {
chrome.test.fail("Received unexpected event '" + name + "':" +
JSON.stringify(details));
}
capturedEventData.push({label: label, event: name, details: details});
checkExpectations();
return retval;
}
// Simple array intersection. We use this to filter extraInfoSpec so
// that only the allowed specs are sent to each listener.
function intersect(array1, array2) {
return array1.filter(function(x) { return array2.indexOf(x) != -1; });
}
function initListeners(filter, extraInfoSpec) {
chrome.experimental.webRequest.onBeforeRequest.addListener(
function(details) {
return captureEvent("onBeforeRequest", details);
}, filter, intersect(extraInfoSpec, ["blocking"]));
chrome.experimental.webRequest.onBeforeSendHeaders.addListener(
function(details) {
return captureEvent("onBeforeSendHeaders", details);
}, filter, intersect(extraInfoSpec, ["blocking", "requestHeaders"]));
chrome.experimental.webRequest.onSendHeaders.addListener(
function(details) {
return captureEvent("onSendHeaders", details);
}, filter, intersect(extraInfoSpec, ["requestHeaders"]));
chrome.experimental.webRequest.onAuthRequired.addListener(
function(details) {
return captureEvent("onAuthRequired", details);
}, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"]));
chrome.experimental.webRequest.onResponseStarted.addListener(
function(details) {
return captureEvent("onResponseStarted", details);
}, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"]));
chrome.experimental.webRequest.onBeforeRedirect.addListener(
function(details) {
return captureEvent("onBeforeRedirect", details);
}, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"]));
chrome.experimental.webRequest.onCompleted.addListener(
function(details) {
return captureEvent("onCompleted", details);
}, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"]));
chrome.experimental.webRequest.onErrorOccurred.addListener(
function(details) {
return captureEvent("onErrorOccurred", details);
}, filter);
}
function removeListeners() {
function helper(event) {
// Note: We're poking at the internal event data, but it's easier than
// the alternative. If this starts failing, we just need to update this
// helper.
for (var cb in event.callbackMap_) {
event.removeListener(cb);
}
chrome.test.assertFalse(event.hasListeners());
}
helper(chrome.experimental.webRequest.onBeforeRequest);
helper(chrome.experimental.webRequest.onBeforeSendHeaders);
helper(chrome.experimental.webRequest.onAuthRequired);
helper(chrome.experimental.webRequest.onSendHeaders);
helper(chrome.experimental.webRequest.onResponseStarted);
helper(chrome.experimental.webRequest.onBeforeRedirect);
helper(chrome.experimental.webRequest.onCompleted);
helper(chrome.experimental.webRequest.onErrorOccurred);
}
{
"name": "webRequest",
"version": "1.0",
"description": "Tests the webRequest API.",
"permissions": ["experimental", "tabs", "http://*/*"]
}
<script src="framework.js">
</script>
<script>
// Constants as functions, not to be called until after runTests.
function getURLEchoUserAgent() {
return getServerURL('echoheader?User-Agent');
}
runTests([
// Navigates to a page with subresources, with a blocking handler that
// cancels the page request. The page will not load, and we should not
// see the subresources.
function complexLoadCancelled() {
expect(
[ // events
{ label: "onBeforeRequest",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURL("complexLoad/a.html"),
frameUrl: getURL("complexLoad/a.html")
},
retval: {cancel: true}
},
// Cancelling is considered an error.
{ label: "onErrorOccurred",
event: "onErrorOccurred",
details: {
url: getURL("complexLoad/a.html"),
fromCache: false,
error: "net::ERR_EMPTY_RESPONSE"
// Request to chrome-extension:// url has no IP.
}
},
],
[ // event order
["onBeforeRequest"]
],
{}, // filter
["blocking"]);
navigateAndWait(getURL("complexLoad/a.html"));
},
// Navigates to a page with a blocking handler that redirects to a different
// page.
// TODO(mpcomplete): We should see an onBeforeRedirect as well, but our
// process switching logic cancels the original redirect request and
// starts a new one instead. See http://crbug.com/79520.
function complexLoadRedirected() {
expect(
[ // events
{ label: "onBeforeRequest-1",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURL("complexLoad/a.html"),
frameUrl: getURL("complexLoad/a.html")
},
retval: {redirectUrl: getURL("simpleLoad/a.html")}
},
{ label: "onErrorOccurred-1",
event: "onErrorOccurred",
details: {
url: getURL("complexLoad/a.html"),
fromCache: false,
error: "net::ERR_ABORTED"
// Request to chrome-extension:// url has no IP.
}
},
{ label: "onBeforeRequest-2",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURL("simpleLoad/a.html"),
frameUrl: getURL("simpleLoad/a.html"),
},
},
{ label: "onResponseStarted",
event: "onResponseStarted",
details: {
url: getURL("simpleLoad/a.html"),
fromCache: false,
statusCode: 200
// Request to chrome-extension:// url has no IP.
}
},
{ label: "onCompleted",
event: "onCompleted",
details: {
url: getURL("simpleLoad/a.html"),
fromCache: false,
statusCode: 200
// Request to chrome-extension:// url has no IP.
}
},
],
[ // event order
["onBeforeRequest-1", "onErrorOccurred-1", "onBeforeRequest-2",
"onResponseStarted", "onCompleted"],
],
{}, // filter
["blocking"]);
navigateAndWait(getURL("complexLoad/a.html"));
},
// Loads a testserver page that echoes the User-Agent header that was
// sent to fetch it. We modify the outgoing User-Agent in
// onBeforeSendHeaders, so we should see that modified version.
function modifyRequestHeaders() {
expect(
[ // events
{ label: "onBeforeRequest",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURLEchoUserAgent(),
frameUrl: getURLEchoUserAgent()
}
},
{ label: "onBeforeSendHeaders",
event: "onBeforeSendHeaders",
details: {
url: getURLEchoUserAgent(),
// Note: no requestHeaders because we don't ask for them.
},
retval: {requestHeaders: [{name: "User-Agent", value: "FoobarUA"}]}
},
{ label: "onSendHeaders",
event: "onSendHeaders",
details: {
url: getURLEchoUserAgent()
}
},
{ label: "onResponseStarted",
event: "onResponseStarted",
details: {
url: getURLEchoUserAgent(),
fromCache: false,
statusCode: 200,
ip: "127.0.0.1"
}
},
{ label: "onCompleted",
event: "onCompleted",
details: {
url: getURLEchoUserAgent(),
fromCache: false,
statusCode: 200,
ip: "127.0.0.1"
}
},
],
[ // event order
["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
"onResponseStarted", "onCompleted"]
],
{}, ["blocking"]);
// Check the page content for our modified User-Agent string.
navigateAndWait(getURLEchoUserAgent(), function() {
chrome.test.listenOnce(chrome.extension.onRequest, function(request) {
chrome.test.assertTrue(request.pass, "Request header was not set.");
});
chrome.tabs.executeScript(tabId,
{
code: "chrome.extension.sendRequest(" +
"{pass: document.body.innerText.indexOf('FoobarUA') >= 0});"
});
});
},
]);
</script>
<script src="framework.js">
</script>
<script>
// Constants as functions, not to be called until after runTests.
function getURLHttpSimpleLoad() {
return getServerURL('files/extensions/api_test/webrequest/simpleLoad/a.html');
}
function getURLHttpSimpleLoadRedirect() {
return getServerURL('server-redirect?'+getURLHttpSimpleLoad());
}
runTests([
// Navigates to a blank page.
function simpleLoad() {
expect(
[ // events
{ label: "a-onBeforeRequest",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURL("simpleLoad/a.html"),
frameUrl: getURL("simpleLoad/a.html")
}
},
{ label: "a-onResponseStarted",
event: "onResponseStarted",
details: {
url: getURL("simpleLoad/a.html"),
statusCode: 200,
fromCache: false
// Request to chrome-extension:// url has no IP.
}
},
{ label: "a-onCompleted",
event: "onCompleted",
details: {
url: getURL("simpleLoad/a.html"),
statusCode: 200,
fromCache: false
// Request to chrome-extension:// url has no IP.
}
},
],
[ // event order
["a-onBeforeRequest", "a-onResponseStarted", "a-onCompleted"] ]);
navigateAndWait(getURL("simpleLoad/a.html"));
},
// Navigates to a blank page via HTTP. Only HTTP requests get the
// onBeforeSendHeaders event.
function simpleLoadHttp() {
expect(
[ // events
{ label: "onBeforeRequest-1",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURLHttpSimpleLoadRedirect(),
frameUrl: getURLHttpSimpleLoadRedirect()
}
},
{ label: "onBeforeSendHeaders-1",
event: "onBeforeSendHeaders",
details: {
url: getURLHttpSimpleLoadRedirect(),
requestHeadersValid: true
}
},
{ label: "onSendHeaders-1",
event: "onSendHeaders",
details: {
url: getURLHttpSimpleLoadRedirect(),
requestHeadersValid: true
}
},
{ label: "onBeforeRedirect",
event: "onBeforeRedirect",
details: {
url: getURLHttpSimpleLoadRedirect(),
redirectUrl: getURLHttpSimpleLoad(),
statusCode: 301,
responseHeadersExist: true,
ip: "127.0.0.1",
fromCache: false,
statusLine: "HTTP/1.0 301 Moved Permanently"
}
},
{ label: "onBeforeRequest-2",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURLHttpSimpleLoad(),
frameUrl: getURLHttpSimpleLoad()
}
},
{ label: "onBeforeSendHeaders-2",
event: "onBeforeSendHeaders",
details: {
url: getURLHttpSimpleLoad(),
requestHeadersValid: true
}
},
{ label: "onSendHeaders-2",
event: "onSendHeaders",
details: {
url: getURLHttpSimpleLoad(),
requestHeadersValid: true
}
},
{ label: "onResponseStarted",
event: "onResponseStarted",
details: {
url: getURLHttpSimpleLoad(),
statusCode: 200,
responseHeadersExist: true,
ip: "127.0.0.1",
fromCache: false,
statusLine: "HTTP/1.0 200 OK",
}
},
{ label: "onCompleted",
event: "onCompleted",
details: {
url: getURLHttpSimpleLoad(),
statusCode: 200,
ip: "127.0.0.1",
fromCache: false,
responseHeadersExist: true,
statusLine: "HTTP/1.0 200 OK"
}
}
],
[ // event order
["onBeforeRequest-1", "onBeforeSendHeaders-1", "onSendHeaders-1",
"onBeforeRedirect",
"onBeforeRequest-2", "onBeforeSendHeaders-2", "onSendHeaders-2",
"onResponseStarted", "onCompleted"] ],
{}, // filter
["requestHeaders", "responseHeaders", "statusLine"]);
navigateAndWait(getURLHttpSimpleLoadRedirect());
},
// Navigates to a non-existing page.
function nonExistingLoad() {
expect(
[ // events
{ label: "onBeforeRequest",
event: "onBeforeRequest",
details: {
method: "GET",
tabId: tabId,
type: "main_frame",
url: getURL("does_not_exist.html"),
frameUrl: getURL("does_not_exist.html")
}
},
{ label: "onErrorOccurred",
event: "onErrorOccurred",
details: {
url: getURL("does_not_exist.html"),
fromCache: false,
error: "net::ERR_FILE_NOT_FOUND",
// Request to chrome-extension:// url has no IP.
}
},
],
[ // event order
["onBeforeRequest", "onErrorOccurred"] ]);
navigateAndWait(getURL("does_not_exist.html"));
},
]);
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment