summaryrefslogtreecommitdiffstats
path: root/tools/page_cycler
diff options
context:
space:
mode:
authorslamm@google.com <slamm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-18 02:40:39 +0000
committerslamm@google.com <slamm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-18 02:40:39 +0000
commite8e3eea260001a067f069d8da98584c2f6fc44fa (patch)
tree8b3c882efaea296f1365c1b838e45df11c76cda0 /tools/page_cycler
parent5bd6feee0b7185ee2b07f245f7a89a74f8f2ef94 (diff)
downloadchromium_src-e8e3eea260001a067f069d8da98584c2f6fc44fa.zip
chromium_src-e8e3eea260001a067f069d8da98584c2f6fc44fa.tar.gz
chromium_src-e8e3eea260001a067f069d8da98584c2f6fc44fa.tar.bz2
Add Web Page Replay test to page cycler.
- Add supporting WPR extension. BUG= TEST= Review URL: http://codereview.chromium.org/9956045 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132730 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/page_cycler')
-rw-r--r--tools/page_cycler/common/head.js10
-rw-r--r--tools/page_cycler/common/report.html16
-rw-r--r--tools/page_cycler/webpagereplay/README15
-rw-r--r--tools/page_cycler/webpagereplay/extension/background.html2
-rw-r--r--tools/page_cycler/webpagereplay/extension/background.js328
-rw-r--r--tools/page_cycler/webpagereplay/extension/content.js14
-rw-r--r--tools/page_cycler/webpagereplay/extension/manifest.json26
-rw-r--r--tools/page_cycler/webpagereplay/extension/page_cycler.js54
-rw-r--r--tools/page_cycler/webpagereplay/extension/start.js89
-rw-r--r--tools/page_cycler/webpagereplay/start.html26
-rw-r--r--tools/page_cycler/webpagereplay/start.js123
-rw-r--r--tools/page_cycler/webpagereplay/tests/2012Q2.js77
12 files changed, 771 insertions, 9 deletions
diff --git a/tools/page_cycler/common/head.js b/tools/page_cycler/common/head.js
index f12fb24..dddaa7ee 100644
--- a/tools/page_cycler/common/head.js
+++ b/tools/page_cycler/common/head.js
@@ -26,13 +26,11 @@ function __pages() { // fetch lazily
return this.data;
}
function __get_timings() {
- if (sessionStorage == null)
+ if (sessionStorage != null &&
+ sessionStorage.getItem("__pc_timings") != null) {
+ return sessionStorage["__pc_timings"];
+ } else {
return __get_cookie("__pc_timings");
- else {
- if (sessionStorage.getItem("__pc_timings") == null)
- return "";
- else
- return sessionStorage["__pc_timings"];
}
}
function __set_timings(timings) {
diff --git a/tools/page_cycler/common/report.html b/tools/page_cycler/common/report.html
index 221d8ab..9833fbe 100644
--- a/tools/page_cycler/common/report.html
+++ b/tools/page_cycler/common/report.html
@@ -79,6 +79,7 @@ document.write("</table>");
r.vari = r.vari / (ary.length - 1);
r.stdd = Math.sqrt(r.vari);
+ r.errp = r.stdd / Math.sqrt((ary.length - 1) / 2) / r.mean * 100;
return r;
}
@@ -88,7 +89,14 @@ document.write("</table>");
if (linkify) {
var anchor = doc.createElement("A");
- anchor.href = text + "/index.html?skip=true";
+ if (text.indexOf('http://localhost:') == 0 ||
+ text.indexOf('file://') == 0) {
+ // URLs for page cycler HTTP and file tests.
+ anchor.href = text + "/index.html?skip=true";
+ } else {
+ // For Web Page Replay, URLs are same as recorded pages.
+ anchor.href = text;
+ }
anchor.appendChild(doc.createTextNode(text));
td.appendChild(anchor);
}
@@ -113,7 +121,7 @@ document.write("</table>");
function showReport() {
var tbody = document.getElementById("tbody");
- var colsums = [0,0,0,0];
+ var colsums = [0,0,0,0,0];
var timeVals = getTimeVals();
for (var i = 0; i < timeVals.length; ++i) {
var tr = document.createElement("TR");
@@ -125,6 +133,7 @@ document.write("</table>");
appendTableCol(tr, r.max.toFixed(2));
appendTableCol(tr, r.mean.toFixed(2));
appendTableCol(tr, r.stdd.toFixed(2));
+ appendTableCol(tr, r.errp.toFixed(2));
//appendTableCol(tr, r.chi2.toFixed(2));
for (var j = 0; j < timeVals[i].length; ++j) {
@@ -138,6 +147,7 @@ document.write("</table>");
colsums[1] = colsums[1] + r.max;
colsums[2] = colsums[2] + r.mean;
colsums[3] = colsums[3] + r.stdd;
+ colsums[4] = colsums[4] + r.errp;
tbody.appendChild(tr);
}
@@ -163,6 +173,7 @@ document.write("</table>");
<th>Max</th>
<th>Mean</th>
<th>Std.d</th>
+ <th>Err %</th>
<th colspan="10">Runs</th>
</tr>
</thead>
@@ -170,4 +181,3 @@ document.write("</table>");
</table>
</body>
</html>
-
diff --git a/tools/page_cycler/webpagereplay/README b/tools/page_cycler/webpagereplay/README
new file mode 100644
index 0000000..6fce3e9
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/README
@@ -0,0 +1,15 @@
+Page Cycler Tests with Web Page Replay
+---------------------------------------------------------------------------
+Web Page Replay is a proxy that can record and "play" web pages with
+simulated network characteristics -- without having to edit the pages
+by hand. With WPR, tests can use "real" web content, and catch
+performance issues that may result from introducing network delays and
+bandwidth throttling.
+
+The Chromium extension here is used to load URLs, record the times,
+and set the appropriate cookies for src/tools/page_cycler/common/report.html.
+
+For more information:
+https://sites.google.com/a/chromium.org/dev/developers/testing/page-cyclers-wpr
+https://sites.google.com/a/chromium.org/dev/developers/testing/page-cyclers
+http://code.google.com/p/web-page-replay
diff --git a/tools/page_cycler/webpagereplay/extension/background.html b/tools/page_cycler/webpagereplay/extension/background.html
new file mode 100644
index 0000000..61a2902
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/background.html
@@ -0,0 +1,2 @@
+<script src="background.js"></script>
+<script src="page_cycler.js"></script>
diff --git a/tools/page_cycler/webpagereplay/extension/background.js b/tools/page_cycler/webpagereplay/extension/background.js
new file mode 100644
index 0000000..207979e
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/background.js
@@ -0,0 +1,328 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// start.js sends a "start" message to set this.
+window.benchmarkConfiguration = {};
+
+// The callback (e.g. report writer) is set via AddBenchmarckCallback.
+window.benchmarkCallback;
+
+// Url to load before loading target page.
+var kWaitUrl = "http://wprwprwpr/web-page-replay-generate-200";
+
+// Constant StatCounter Names
+var kTcpReadBytes = "tcp.read_bytes";
+var kTcpWriteBytes = "tcp.write_bytes";
+var kRequestCount = "HttpNetworkTransaction.Count";
+var kConnectCount = "tcp.connect";
+
+function CHECK(expr, comment) {
+ if (!expr) {
+ console.log(comment);
+ alert(comment);
+ }
+}
+
+function Result() {
+ var me_ = this;
+ this.url = "";
+ this.firstPaintTime = 0;
+ this.readBytesKB = 0;
+ this.writeBytesKB = 0;
+ this.numRequests = 0;
+ this.numConnects = 0;
+ this.timing = {}; // window.performance.timing
+ this.getTotalTime = function() {
+ var totalTime = 0
+ if (me_.timing.navigationStart && me_.timing.loadEventEnd) {
+ totalTime = me_.timing.loadEventEnd - me_.timing.navigationStart;
+ }
+ CHECK(totalTime >= 0);
+ return totalTime;
+ }
+}
+
+// Collect all the results for a session (i.e. different pages).
+function ResultsCollection() {
+ var results_ = [];
+ var pages_ = [];
+ var pageResults_ = {};
+
+ this.addResult = function(result) {
+ results_.push(result);
+ var url = result.url;
+ if (!(url in pageResults_)) {
+ pages_.push(url);
+ pageResults_[url] = [];
+ }
+ pageResults_[url].push(result);
+ }
+
+ this.getPages = function() {
+ return pages_;
+ }
+
+ this.getResults = function() {
+ return results_;
+ }
+
+ this.getTotalTimes = function() {
+ return results_.map(function (t) { return t.getTotalTime(); });
+ }
+}
+
+// Load a url in the default tab and record the time.
+function PageLoader(url, resultReadyCallback) {
+ var me_ = this;
+ var url_ = url;
+ var resultReadyCallback_ = resultReadyCallback;
+
+ // If it record mode, wait a little longer for lazy loaded resources.
+ var postLoadGraceMs_ = window.isRecordMode ? 5000 : 0;
+ var loadInterval_ = window.loadInterval;
+ var checkInterval_ = window.checkInterval;
+ var timeout_ = window.timeout;
+ var maxLoadChecks_ = window.maxLoadChecks;
+
+ var preloadFunc_;
+ var timeoutId_;
+ var isFinished_;
+ var result_;
+
+ var initialReadBytes_;
+ var initialWriteBytes_;
+ var initialRequestCount_;
+ var initialConnectCount_;
+
+ this.result = function() { return result_; };
+
+ this.run = function() {
+ timeoutId_ = null;
+ isFinished_ = false;
+ result_ = null;
+ initialReadBytes_ = chrome.benchmarking.counter(kTcpReadBytes);
+ initialWriteBytes_ = chrome.benchmarking.counter(kTcpWriteBytes);
+ initialRequestCount_ = chrome.benchmarking.counter(kRequestCount);
+ initialConnectCount_ = chrome.benchmarking.counter(kConnectCount);
+
+ if (me_.preloadFunc_) {
+ me_.preloadFunc_(me_.load_);
+ } else {
+ me_.load_();
+ }
+ };
+
+ this.setClearAll = function() {
+ me_.preloadFunc_ = me_.clearAll_;
+ };
+
+ this.setClearConnections = function() {
+ me_.preloadFunc_ = me_.clearConnections_;
+ };
+
+ this.clearAll_ = function(callback) {
+ chrome.tabs.getSelected(null, function(tab) {
+ chrome.tabs.update(tab.id, {"url": kWaitUrl}, function() {
+ chrome.benchmarking.clearHostResolverCache();
+ chrome.benchmarking.clearPredictorCache();
+ chrome.benchmarking.closeConnections();
+ var dataToRemove = {
+ "appcache": true,
+ "cache": true,
+ "cookies": true,
+ "downloads": true,
+ "fileSystems": true,
+ "formData": true,
+ "history": true,
+ "indexedDB": true,
+ "localStorage": true,
+ "passwords": true,
+ "pluginData": true,
+ "webSQL": true
+ };
+ // Add any items new to the API.
+ for (var prop in chrome.browsingData) {
+ var dataName = prop.replace("remove", "");
+ if (dataName && dataName != prop) {
+ dataName = dataName.charAt(0).toLowerCase() +
+ dataName.substr(1);
+ if (!dataToRemove.hasOwnProperty(dataName)) {
+ console.log("New browsingData API item: " + dataName);
+ dataToRemove[dataName] = true;
+ }
+ }
+ }
+ chrome.browsingData.remove({}, dataToRemove, callback);
+ });
+ });
+ };
+
+ this.clearConnections_ = function(callback) {
+ chrome.benchmarking.closeConnections();
+ callback();
+ };
+
+ this.load_ = function() {
+ console.log("LOAD started: " + url_);
+ setTimeout(function() {
+ chrome.extension.onRequest.addListener(me_.finishLoad_);
+ timeoutId_ = setTimeout(function() {
+ me_.finishLoad_({"loadTimes": null, "timing": null});
+ }, timeout_);
+ chrome.tabs.getSelected(null, function(tab) {
+ chrome.tabs.update(tab.id, {"url": url_});
+ });
+ }, loadInterval_);
+ };
+
+ this.finishLoad_ = function(msg) {
+ if (!isFinished_) {
+ isFinished_ = true;
+ clearTimeout(timeoutId_);
+ chrome.extension.onRequest.removeListener(me_.finishLoad_);
+ me_.saveResult_(msg.loadTimes, msg.timing);
+ }
+ };
+
+ this.saveResult_ = function(loadTimes, timing) {
+ result_ = new Result()
+ result_.url = url_;
+ if (!loadTimes || !timing) {
+ console.log("LOAD INCOMPLETE: " + url_);
+ } else {
+ console.log("LOAD complete: " + url_);
+ result_.timing = timing;
+ var baseTime = timing.navigationStart;
+ CHECK(baseTime);
+ result_.firstPaintTime = Math.max(0,
+ Math.round((1000.0 * loadTimes.firstPaintTime) - baseTime));
+ }
+ result_.readBytesKB = (chrome.benchmarking.counter(kTcpReadBytes) -
+ initialReadBytes_) / 1024;
+ result_.writeBytesKB = (chrome.benchmarking.counter(kTcpWriteBytes) -
+ initialWriteBytes_) / 1024;
+ result_.numRequests = (chrome.benchmarking.counter(kRequestCount) -
+ initialRequestCount_);
+ result_.numConnects = (chrome.benchmarking.counter(kConnectCount) -
+ initialConnectCount_);
+ setTimeout(function() { resultReadyCallback_(me_); }, postLoadGraceMs_);
+ };
+}
+
+// Load page sets and prepare performance results.
+function SessionLoader(resultsReadyCallback) {
+ var me_ = this;
+ var resultsReadyCallback_ = resultsReadyCallback;
+ var pageSets_ = benchmarkConfiguration.pageSets;
+ var iterations_ = window.iterations;
+ var retries_ = window.retries;
+
+ var pageLoaders_ = [];
+ var resultsCollection_ = new ResultsCollection();
+ var loaderIndex_ = 0;
+ var retryIndex_ = 0;
+ var iterationIndex_ = 0;
+
+ this.run = function() {
+ me_.createLoaders_();
+ me_.loadPage_();
+ }
+
+ this.getResultsCollection = function() {
+ return resultsCollection_;
+ }
+
+ this.createLoaders_ = function() {
+ // Each url becomes one benchmark.
+ for (var i = 0; i < pageSets_.length; i++) {
+ for (var j = 0; j < pageSets_[i].length; j++) {
+ // Remove extra space at the beginning or end of a url.
+ var url = pageSets_[i][j].trim();
+ // Alert about and ignore blank page which does not get loaded.
+ if (url == "about:blank") {
+ alert("blank page loaded!");
+ } else if (!url.match(/https?:\/\//)) {
+ // Alert about url that is not in scheme http:// or https://.
+ alert("Skipping url without http:// or https://: " + url);
+ } else {
+ var loader = new PageLoader(url, me_.handleResult_)
+ if (j == 0) {
+ // Clear all browser data for the first page in a sub list.
+ loader.setClearAll();
+ } else {
+ // Otherwise, only clear the connections.
+ loader.setClearConnections();
+ }
+ pageLoaders_.push(loader);
+ }
+ }
+ }
+ }
+
+ this.loadPage_ = function() {
+ console.log("LOAD url " + (loaderIndex_ + 1) + " of " +
+ pageLoaders_.length +
+ ", iteration " + (iterationIndex_ + 1) + " of " +
+ iterations_);
+ pageLoaders_[loaderIndex_].run();
+ }
+
+ this.handleResult_ = function(loader) {
+ var result = loader.result();
+ resultsCollection_.addResult(result);
+ var totalTime = result.getTotalTime();
+ if (!totalTime && retryIndex_ < retries_) {
+ retryIndex_++;
+ console.log("LOAD retry, " + retryIndex_);
+ } else {
+ retryIndex_ = 0;
+ console.log("RESULTS url " + (loaderIndex_ + 1) + " of " +
+ pageLoaders_.length +
+ ", iteration " + (iterationIndex_ + 1) + " of " +
+ iterations_ + ": " + totalTime);
+ loaderIndex_++;
+ if (loaderIndex_ >= pageLoaders_.length) {
+ iterationIndex_++;
+ if (iterationIndex_ < iterations_) {
+ loaderIndex_ = 0;
+ } else {
+ resultsReadyCallback_(me_);
+ return;
+ }
+ }
+ }
+ me_.loadPage_();
+ }
+}
+
+function AddBenchmarkCallback(callback) {
+ window.benchmarkCallback = callback;
+}
+
+function Run() {
+ window.checkInterval = 500;
+ window.loadInterval = 1000;
+ window.timeout = 20000; // max ms before killing page.
+ window.retries = 0;
+ window.isRecordMode = benchmarkConfiguration.isRecordMode;
+ if (window.isRecordMode) {
+ window.iterations = 1;
+ window.timeout = 40000;
+ window.retries = 2;
+ } else {
+ window.iterations = benchmarkConfiguration["iterations"] || 3;
+ }
+ var sessionLoader = new SessionLoader(benchmarkCallback);
+ console.log("pageSets: " + JSON.stringify(benchmarkConfiguration.pageSets));
+ sessionLoader.run();
+}
+
+chrome.extension.onConnect.addListener(function(port) {
+ port.onMessage.addListener(function(data) {
+ if (data.message == "start") {
+ window.benchmarkConfiguration = data.benchmark;
+ Run()
+ }
+ });
+}); \ No newline at end of file
diff --git a/tools/page_cycler/webpagereplay/extension/content.js b/tools/page_cycler/webpagereplay/extension/content.js
new file mode 100644
index 0000000..69713c3
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/content.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Hook onload and then add a timeout to send timing information
+// after the onload has completed.
+window.addEventListener('load', function() {
+ window.setTimeout(function() {
+ chrome.extension.sendRequest({
+ "timing": window.performance.timing,
+ "loadTimes": chrome.loadTimes(),
+ });
+ }, 0);
+}, false);
diff --git a/tools/page_cycler/webpagereplay/extension/manifest.json b/tools/page_cycler/webpagereplay/extension/manifest.json
new file mode 100644
index 0000000..a775768
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/manifest.json
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+{
+ "name": "Chromium Page Cycler with Web Page Replay.",
+ "version": "1.0.0.0",
+ "description": "Chromium Page Cycler with Web Page Replay.",
+ "background_page": "background.html",
+ "content_scripts": [
+ { "matches": ["file:///*/start.html*"],
+ "js": ["start.js"],
+ "run_at": "document_idle"
+ },
+ { "matches": ["http://*/*", "https://*/*"],
+ "js": ["content.js"],
+ "run_at": "document_start"
+ }
+ ],
+ "permissions": [
+ "browsingData",
+ "cookies",
+ "experimental",
+ "tabs",
+ "<all_urls>"
+ ]
+}
diff --git a/tools/page_cycler/webpagereplay/extension/page_cycler.js b/tools/page_cycler/webpagereplay/extension/page_cycler.js
new file mode 100644
index 0000000..76869b2
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/page_cycler.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Returns the sum of all values in the array.
+Array.sum = function(array) {
+ var sum = 0;
+ for (var i = array.length - 1; i >= 0; i--) {
+ sum += array[i];
+ }
+ return sum;
+};
+
+
+function WriteReport(sessionLoader) {
+ var iterations = window.iterations;
+ var reportUrl = window.benchmarkConfiguration.reportUrl;
+ var resultsCollection = sessionLoader.getResultsCollection();
+ var times = resultsCollection.getTotalTimes();
+ var pages = resultsCollection.getPages();
+
+ reportUrl += "?n=" + iterations;
+ reportUrl += "&i=" + iterations * pages.length; // "cycles"
+ reportUrl += "&td=" + Array.sum(times); // total time
+ reportUrl += "&tf=" + 0; // fudge time
+ console.log('reportUrl: ' + reportUrl);
+ chrome.cookies.set({
+ "url": reportUrl,
+ "name": "__pc_done",
+ "value": "1",
+ "path": "/",
+ });
+ chrome.cookies.set({
+ "url": reportUrl,
+ "name": "__pc_pages",
+ "value": pages.map(function(x) {
+ return x.replace(/=/g, "%3D");
+ }).join(","),
+ "path": "/",
+ });
+ chrome.cookies.set({
+ "url": reportUrl,
+ "name": "__pc_timings",
+ "value": times.join(","),
+ "path": "/",
+ });
+
+ chrome.tabs.getSelected(null, function(tab) {
+ console.log("Navigate to the report.");
+ chrome.tabs.update(tab.id, {"url": reportUrl}, null);
+ });
+}
+
+AddBenchmarkCallback(WriteReport);
diff --git a/tools/page_cycler/webpagereplay/extension/start.js b/tools/page_cycler/webpagereplay/extension/start.js
new file mode 100644
index 0000000..3250edb
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/extension/start.js
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+document.cookie = "__navigated_to_report=0; path=/";
+document.cookie = "__pc_done=0; path=/";
+document.cookie = "__pc_timings=; path=/";
+
+function dirname(path) {
+ var match = path.match(/(.*)\//);
+ if (match) {
+ return match[1];
+ } else {
+ return ".";
+ }
+}
+
+function IsWprRecordMode() {
+ var kStatusUrl = "http://wprwprwpr/web-page-replay-command-status";
+ var isRecordMode;
+ var xhr = new XMLHttpRequest();
+ var useAsync = false;
+ xhr.open("GET", kStatusUrl, useAsync);
+ xhr.timeout = 500;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4 && xhr.status == 200) {
+ var status = JSON.parse(xhr.responseText);
+ isRecordMode = status["is_record_mode"];
+ console.log("WPR record mode?: " + isRecordMode);
+ }
+ };
+ try {
+ xhr.send();
+ } catch(e) {
+ throw "Web Page Replay is not responding. Start WPR to continue."
+ }
+ return isRecordMode;
+}
+
+
+function TryStart() {
+ console.log("try start");
+ var status_element = document.getElementById("status");
+
+ var config_json;
+ var config;
+ try {
+ config_json = document.getElementById("json").textContent;
+ config = JSON.parse(config_json);
+ } catch(err) {
+ console.log("Bad json data: " + config_json);
+ status_element.textContent = "Exception: " + err + "\njson data: " +
+ config_json;
+ return;
+ }
+ var isRecordMode = false;
+ try {
+ isRecordMode = IsWprRecordMode();
+ } catch (err) {
+ status_element.textContent = err;
+ setTimeout(TryStart, 5000);
+ return;
+ }
+
+ if (!config["shouldStart"]) {
+ status_element.textContent =
+ "Press 'Start' to continue (or load this page with 'auto=1').";
+ return;
+ }
+
+ try {
+ var reportDir = dirname(dirname(window.location.pathname)) + "/common";
+ config["reportUrl"] = "file://" + reportDir + "/report.html";
+ config["isRecordMode"] = isRecordMode;
+ var port = chrome.extension.connect();
+ port.postMessage({message: "start", benchmark: config});
+ console.log("sending start message: page count, " +
+ config["pageSets"].length);
+ } catch(err) {
+ console.log("TryStart retrying after exception: " + err);
+ status_element.textContent = "Exception: " + err;
+ setTimeout(TryStart, 1000);
+ return;
+ }
+ status_element.textContent = "STARTING";
+}
+
+// We wait before starting the test just to let chrome warm up better.
+setTimeout(TryStart, 250);
diff --git a/tools/page_cycler/webpagereplay/start.html b/tools/page_cycler/webpagereplay/start.html
new file mode 100644
index 0000000..d263a35
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/start.html
@@ -0,0 +1,26 @@
+<html>
+<!-- Copyright (c) 2012 The Chromium Authors. All rights reserved. -->
+<!-- Use of this source code is governed by a BSD-style license that can be -->
+<!-- found in the LICENSE file. -->
+<head>
+<style>
+ .labeledtextarea * {
+ vertical-align: middle;
+ }
+</style>
+</head>
+<body>
+<h3>Page Cycler: Web Page Replay Test</h3>
+
+<p class=labeledtextarea>
+<label for="status">Status:</label>
+<textarea id=status style="width:40em;height:3em;"></textarea>
+</p>
+
+<hr>
+<p>
+<div id=startform></div>
+<textarea id=json style="visibility: hidden;"></textarea>
+<script src="start.js"></script>
+</body>
+</html>
diff --git a/tools/page_cycler/webpagereplay/start.js b/tools/page_cycler/webpagereplay/start.js
new file mode 100644
index 0000000..6ff5e6d
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/start.js
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// webpagereplay/start.js - Start Web Page Replay (WPR) test.
+//
+// This script is included by webpagereplay/start.html.
+// The query parameter "test=TEST_NAME" is required to load the
+// test configuration from webpagereplay/tests/TEST_NAME.js
+// That JavaScript file defines a global, "pageSets", as a list of lists:
+// [ [url_1, url_2], [url_3], ...],
+// - Before each sublist:
+// Run chrome.browingData.remove and close the connections.
+// - Before each url in a sublist:
+// Close the connections.
+//
+// These WPR tests use a Chrome extension to load the test URLs.
+// The extension loads the test configuration via a DOM elemment
+// (id=json). This script sets the content of that DOM element.
+//
+// The test runs immediately after being loaded.
+//
+
+
+var options = location.search.substring(1).split('&');
+function getopt(name) {
+ var r = new RegExp('^' + name + '=');
+ for (i = 0; i < options.length; ++i) {
+ if (options[i].match(r)) {
+ return options[i].substring(name.length + 1);
+ }
+ }
+ return null;
+}
+
+function LoadTestConfigurationScript(testUrl, callback) {
+ var testjs = document.createElement('script');
+ testjs.type = 'text/javascript';
+ testjs.async = true;
+ testjs.src = testUrl
+ var s = document.getElementsByTagName('script')[0];
+ testjs.addEventListener('load', callback, false);
+ s.parentNode.insertBefore(testjs, s);
+}
+
+function ReloadIfStuck() {
+ setTimeout(function() {
+ var status = document.getElementById('status');
+ // The status text is set to 'STARTING' by the extension.
+ if (status.textContent != 'STARTING') {
+ console.log('Benchmark stuck? Reloading.');
+ window.location.reload(true);
+ }
+ }, 30000);
+}
+
+function RenderForm() {
+ var form = document.createElement('FORM');
+ form.setAttribute('action', 'start.html');
+
+ var label = document.createTextNode('Iterations: ');
+ form.appendChild(label);
+
+ var input = document.createElement('INPUT');
+ var iterations = getopt('iterations');
+ input.setAttribute('name', 'iterations');
+ input.setAttribute('value', iterations ? iterations : '5');
+ form.appendChild(input);
+
+ form.appendChild(document.createElement('P'));
+
+ var label = document.createTextNode('Test: ');
+ form.appendChild(label);
+
+ var input = document.createElement('INPUT');
+ input.setAttribute('name', 'test');
+ var test = getopt('test');
+ input.setAttribute('value', test ? test : '');
+ form.appendChild(input);
+
+ var input = document.createElement('INPUT');
+ input.setAttribute('name', 'auto');
+ var auto = getopt('auto');
+ input.setAttribute('value', 1);
+ input.setAttribute('type', 'hidden');
+ form.appendChild(input);
+
+ form.appendChild(document.createElement('P'));
+
+ input = document.createElement('INPUT');
+ input.setAttribute('type', 'submit');
+ input.setAttribute('value', 'Start');
+ form.appendChild(input);
+
+ document.getElementById('startform').appendChild(form);
+}
+
+
+var iterations = getopt('iterations');
+var test_name = getopt('test');
+var is_auto_start = getopt('auto');
+
+RenderForm();
+if (test_name) {
+ var testUrl = 'tests/' + test_name + '.js';
+ LoadTestConfigurationScript(testUrl, function() {
+ var testConfig = {};
+ if (iterations) {
+ testConfig['iterations'] = iterations;
+ }
+ // The pageSets global is set by test configuration script.
+ testConfig['pageSets'] = pageSets;
+
+ if (is_auto_start) {
+ testConfig['shouldStart'] = 1;
+ ReloadIfStuck();
+ }
+ // Write testConfig to "json" DOM element for the Chrome extension.
+ document.getElementById('json').textContent = JSON.stringify(testConfig);
+ });
+} else {
+ console.log('Need "test=TEST_NAME" query parameter.');
+}
diff --git a/tools/page_cycler/webpagereplay/tests/2012Q2.js b/tools/page_cycler/webpagereplay/tests/2012Q2.js
new file mode 100644
index 0000000..11bba9f
--- /dev/null
+++ b/tools/page_cycler/webpagereplay/tests/2012Q2.js
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ "pageSets" is a list of lists.
+ - Before each sublist:
+ Run chrome.browingData.remove and close the connections.
+ - Before each url in a sublist:
+ Close the connections.
+*/
+var pageSets = [
+ ["http://superfastpageload/web-page-replay-generate-200"],
+
+ // Load url pairs without clearing browser data (e.g. cache) in-between.
+ ["http://go.com/",
+ "http://espn.go.com/"],
+ ["http://www.amazon.com/",
+ "http://www.amazon.com/Kindle-Fire-Amazon-Tablet/dp/B0051VVOB2/"],
+ ["http://www.baidu.com/",
+ "http://www.baidu.com/s?wd=obama"],
+ ["http://www.bing.com/",
+ "http://www.bing.com/search?q=cars"],
+ ["http://www.ebay.com/",
+ "http://fashion.ebay.com/womens-clothing"],
+ ["http://www.google.com/",
+ "http://www.google.com/search?q=dogs"],
+ ["http://www.yandex.ru/",
+ "http://yandex.ru/yandsearch?text=obama&lr=84"],
+ ["http://www.youtube.com",
+ "http://www.youtube.com/watch?v=xvN9Ri1GmuY&feature=g-sptl&cid=inp-hs-edt"],
+
+ ["http://ameblo.jp/"],
+ ["http://en.rakuten.co.jp/"],
+ ["http://en.wikipedia.org/wiki/Lady_gaga"],
+ ["http://news.google.co.in"],
+ ["http://plus.google.com/"], // iframe error (result of no cookies?)
+ ["http://www.163.com/"],
+ ["http://www.apple.com/"],
+ ["http://www.bbc.co.uk/"],
+ ["http://www.cnet.com/"],
+ ["http://www.msn.com/"],
+ ["http://www.nytimes.com/"],
+ ["http://www.taobao.com/"],
+ ["http://www.yahoo.co.jp/"],
+
+ // HTTPS pages.
+ ["https://wordpress.com/"],
+ ["https://www.conduit.com/"],
+ ["https://www.facebook.com",
+ "https://www.facebook.com/barackobama"],
+];
+
+/*
+ // Not included (need further investigation).
+ ["http://twitter.com/BarackObama",
+ "http://twitter.com/search?q=pizza"], // large variance on second page
+ ["http://www.fc2.com/"], // slow
+ ["http://sina.com.cn/"], // slow
+ ["http://www.mail.ru/"], // long load time (30s+)
+ ["http://www.sohu.com/"], // load does not finish (even without WPR)
+
+ // Not included (trimmed pageSets to keep test under 10 minutes).
+ ["http://sfbay.craigslist.org/",
+ "http://sfbay.craigslist.org/search/sss?query=flowers"],
+ ["http://www.flickr.com/",
+ "http://www.flickr.com/photos/tags/flowers"],
+ ["http://www.linkedin.com/",
+ "http://www.linkedin.com/in/jeffweiner08"],
+ ["http://www.yahoo.com/",
+ "http://search.yahoo.com/search?p=disney"],
+ ["http://googleblog.blogspot.com/"],
+ ["http://www.adobe.com/reader/"],
+ ["http://www.cnn.com/"],
+ ["http://www.imdb.com/"],
+ ["http://www.qq.com/"],
+*/