summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/test/perf/page_cycler_test.cc109
-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
-rwxr-xr-xtools/python/google/webpagereplay_utils.py269
14 files changed, 1141 insertions, 17 deletions
diff --git a/chrome/test/perf/page_cycler_test.cc b/chrome/test/perf/page_cycler_test.cc
index 272a4f9..b662660 100644
--- a/chrome/test/perf/page_cycler_test.cc
+++ b/chrome/test/perf/page_cycler_test.cc
@@ -29,12 +29,14 @@
#ifndef NDEBUG
static const int kTestIterations = 2;
static const int kDatabaseTestIterations = 2;
+static const int kWebPageReplayIterations = 2;
#else
static const int kTestIterations = 10;
// For some unknown reason, the DB perf tests are much much slower on the
// Vista perf bot, so we have to cut down the number of iterations to 5
// to make sure each test finishes in less than 10 minutes.
static const int kDatabaseTestIterations = 5;
+static const int kWebPageReplayIterations = 5;
#endif
static const int kIDBTestIterations = 5;
@@ -42,6 +44,11 @@ static const int kIDBTestIterations = 5;
// this URL's server should point to data/page_cycler/.
static const char kBaseUrl[] = "http://localhost:8000/";
+// The following port numbers must match those in
+// src/tools/python/google/webpagereplay_utils.py.
+static const char kWebPageReplayHttpPort[] = "8080";
+static const char kWebPageReplayHttpsPort[] = "8413";
+
namespace {
void PopulateBufferCache(const FilePath& test_dir) {
@@ -158,21 +165,17 @@ class PageCyclerTest : public UIPerfTest {
return num_test_iterations_;
}
- // For HTTP tests, the name must be safe for use in a URL without escaping.
- void RunPageCycler(const char* name, std::wstring* pages,
- std::string* timings, bool use_http) {
+ virtual void GetTestUrl(const char* name, bool use_http, GURL *test_url) {
FilePath test_path = GetDataPath(name);
ASSERT_TRUE(file_util::DirectoryExists(test_path))
<< "Missing test directory " << test_path.value();
-
PopulateBufferCache(test_path);
- GURL test_url;
if (use_http) {
- test_url = GURL(std::string(kBaseUrl) + name + "/start.html");
+ *test_url = GURL(std::string(kBaseUrl) + name + "/start.html");
} else {
test_path = test_path.Append(FILE_PATH_LITERAL("start.html"));
- test_url = net::FilePathToFileURL(test_path);
+ *test_url = net::FilePathToFileURL(test_path);
}
// run N iterations
@@ -182,7 +185,14 @@ class PageCyclerTest : public UIPerfTest {
replacements.SetQuery(
query_string.c_str(),
url_parse::Component(0, query_string.length()));
- test_url = test_url.ReplaceComponents(replacements);
+ *test_url = test_url->ReplaceComponents(replacements);
+ }
+
+ // For HTTP tests, the name must be safe for use in a URL without escaping.
+ void RunPageCycler(const char* name, std::wstring* pages,
+ std::string* timings, bool use_http) {
+ GURL test_url;
+ GetTestUrl(name, use_http, &test_url);
scoped_refptr<TabProxy> tab(GetActiveTab());
ASSERT_TRUE(tab.get());
@@ -443,6 +453,77 @@ class PageCyclerIndexedDatabaseReferenceTest : public PageCyclerReferenceTest {
}
};
+// Web Page Replay is a proxy server to record and serve pages
+// with realistic network delays and bandwidth throttling.
+// runtest.py launches replay.py to support these tests.
+class PageCyclerWebPageReplayTest : public PageCyclerTest {
+ public:
+ PageCyclerWebPageReplayTest() {
+ // These Chrome command-line arguments need to be kept in sync
+ // with src/tools/python/google/webpagereplay_utils.py.
+ FilePath extension_path = GetPageCyclerWprPath("extension");
+ launch_arguments_.AppendSwitchPath(
+ switches::kLoadExtension, extension_path);
+ // TODO(slamm): Instead of kHostResolverRules, add a new switch,
+ // kTestingFixedDnsPort, and configure Web Page Replay to run
+ // a DNS proxy on that port to test Chrome's DNS code.
+ launch_arguments_.AppendSwitchASCII(
+ switches::kHostResolverRules, "MAP * 127.0.0.1");
+ launch_arguments_.AppendSwitchASCII(
+ switches::kTestingFixedHttpPort, kWebPageReplayHttpPort);
+ launch_arguments_.AppendSwitchASCII(
+ switches::kTestingFixedHttpsPort, kWebPageReplayHttpsPort);
+ launch_arguments_.AppendSwitch(switches::kEnableExperimentalExtensionApis);
+ launch_arguments_.AppendSwitch(switches::kEnableStatsTable);
+ launch_arguments_.AppendSwitch(switches::kEnableBenchmarking);
+ launch_arguments_.AppendSwitch(switches::kIgnoreCertificateErrors);
+ launch_arguments_.AppendSwitch(switches::kNoProxyServer);
+ }
+
+ FilePath GetPageCyclerWprPath(const char* name) {
+ FilePath wpr_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &wpr_path);
+ wpr_path = wpr_path.AppendASCII("tools");
+ wpr_path = wpr_path.AppendASCII("page_cycler");
+ wpr_path = wpr_path.AppendASCII("webpagereplay");
+ wpr_path = wpr_path.AppendASCII(name);
+ return wpr_path;
+ }
+
+ virtual int GetTestIterations() OVERRIDE {
+ return kWebPageReplayIterations;
+ }
+
+ virtual void GetTestUrl(const char* name, bool use_http,
+ GURL *test_url) OVERRIDE {
+ FilePath start_path = GetPageCyclerWprPath("start.html");
+
+ // Add query parameters for iterations and test name.
+ const std::string query_string =
+ "iterations=" + base::IntToString(GetTestIterations()) +
+ "&test=" + name +
+ "&auto=1";
+ GURL::Replacements replacements;
+ replacements.SetQuery(
+ query_string.c_str(),
+ url_parse::Component(0, query_string.length()));
+
+ *test_url = net::FilePathToFileURL(start_path);
+ *test_url = test_url->ReplaceComponents(replacements);
+ }
+
+ void RunTest(const char* graph, const char* name) {
+ FilePath test_path = GetPageCyclerWprPath("tests");
+ test_path = test_path.AppendASCII(name);
+ test_path = test_path.ReplaceExtension(FILE_PATH_LITERAL(".js"));
+ ASSERT_TRUE(file_util::PathExists(test_path))
+ << "Missing test file " << test_path.value();
+
+ const bool use_http = false; // always use a file
+ PageCyclerTest::RunTestWithSuffix(graph, name, use_http, "");
+ }
+};
+
// This macro simplifies setting up regular and reference build tests.
#define PAGE_CYCLER_TESTS(test, name, use_http) \
TEST_F(PageCyclerTest, name) { \
@@ -497,6 +578,11 @@ TEST_F(PageCyclerExtensionWebRequestTest, name) { \
RunTest("times", "extension_webrequest", "_extwr", test, false); \
}
+#define PAGE_CYCLER_WEBPAGEREPLAY_TESTS(test, name) \
+TEST_F(PageCyclerWebPageReplayTest, name) { \
+ RunTest("times", test); \
+}
+
// file-URL tests
PAGE_CYCLER_FILE_TESTS("moz", MozFile);
PAGE_CYCLER_EXTENSIONS_FILE_TESTS("moz", MozFile);
@@ -521,6 +607,13 @@ PAGE_CYCLER_HTTP_TESTS("intl2", Intl2Http);
PAGE_CYCLER_HTTP_TESTS("dom", DomHttp);
PAGE_CYCLER_HTTP_TESTS("bloat", BloatHttp);
+// Web Page Replay (simulated network) tests.
+// Windows is unsupported because of issues with loopback adapter and
+// dummynet is unavailable on Vista and above.
+#if !defined(OS_WIN)
+PAGE_CYCLER_WEBPAGEREPLAY_TESTS("2012Q2", 2012Q2);
+#endif
+
// HTML5 database tests
// These tests are _really_ slow on XP/Vista.
#if !defined(OS_WIN)
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/"],
+*/
diff --git a/tools/python/google/webpagereplay_utils.py b/tools/python/google/webpagereplay_utils.py
new file mode 100755
index 0000000..e93ec55
--- /dev/null
+++ b/tools/python/google/webpagereplay_utils.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env python
+# 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.
+
+"""A class to help start/stop a Web Page Replay Server.
+
+The page cycler tests use this module to run Web Page Replay
+(see tools/build/scripts/slave/runtest.py).
+
+If run from the command-line, the module will launch Web Page Replay
+and the specified test:
+
+ ./webpagereplay_utils.py --help # list options
+ ./webpagereplay_utils.py 2012Q2 # run a WPR-enabled test
+"""
+
+import logging
+import optparse
+import os
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import urllib
+
+
+USAGE = '%s [options] CHROME_EXE TEST_NAME' % os.path.basename(sys.argv[0])
+USER_DATA_DIR = '{TEMP}/webpagereplay_utils-chrome'
+
+# The port numbers must match those in chrome/test/perf/page_cycler_test.cc.
+HTTP_PORT = 8080
+HTTPS_PORT = 8413
+
+
+class ReplayError(Exception):
+ """Catch-all exception for the module."""
+ pass
+
+class ReplayNotFoundError(Exception):
+ pass
+
+class ReplayNotStartedError(Exception):
+ pass
+
+
+class ReplayLauncher(object):
+ LOG_FILE = 'log.txt'
+
+ def __init__(self, replay_dir, archive_path, log_dir, replay_options=None):
+ """Initialize ReplayLauncher.
+
+ Args:
+ replay_dir: directory that has replay.py and related modules.
+ archive_path: either a directory that contains WPR archives or,
+ a path to a specific WPR archive.
+ log_dir: where to write log.txt.
+ replay_options: a list of options strings to forward to replay.py.
+ """
+ self.replay_dir = replay_dir
+ self.archive_path = archive_path
+ self.log_dir = log_dir
+ self.replay_options = replay_options if replay_options else []
+
+ self.log_name = os.path.join(self.log_dir, self.LOG_FILE)
+ self.log_fh = None
+ self.proxy_process = None
+
+ self.wpr_py = os.path.join(self.replay_dir, 'replay.py')
+ if not os.path.exists(self.wpr_py):
+ raise ReplayNotFoundError('Path does not exist: %s' % self.wpr_py)
+ self.wpr_options = [
+ '--port', str(HTTP_PORT),
+ '--ssl_port', str(HTTPS_PORT),
+ '--use_closest_match',
+ # TODO(slamm): Add traffic shaping (requires root):
+ # '--net', 'fios',
+ ]
+ self.wpr_options.extend(self.replay_options)
+
+ def _OpenLogFile(self):
+ if not os.path.exists(self.log_dir):
+ os.makedirs(self.log_dir)
+ return open(self.log_name, 'w')
+
+ def StartServer(self):
+ cmd_line = [self.wpr_py]
+ cmd_line.extend(self.wpr_options)
+ # TODO(slamm): Support choosing archive on-the-fly.
+ cmd_line.append(self.archive_path)
+ self.log_fh = self._OpenLogFile()
+ logging.debug('Starting Web-Page-Replay: %s', cmd_line)
+ self.proxy_process = subprocess.Popen(
+ cmd_line, stdout=self.log_fh, stderr=subprocess.STDOUT)
+ if not self.IsStarted():
+ raise ReplayNotStartedError(
+ 'Web Page Replay failed to start. See the log file: ' + self.log_name)
+
+ def IsStarted(self):
+ """Checks to see if the server is up and running."""
+ for _ in range(5):
+ if self.proxy_process.poll() is not None:
+ # The process has exited.
+ break
+ try:
+ up_url = '%s://localhost:%s/web-page-replay-generate-200'
+ http_up_url = up_url % ('http', HTTP_PORT)
+ https_up_url = up_url % ('https', HTTPS_PORT)
+ if (200 == urllib.urlopen(http_up_url, None, {}).getcode() and
+ 200 == urllib.urlopen(https_up_url, None, {}).getcode()):
+ return True
+ except IOError:
+ time.sleep(1)
+ return False
+
+ def StopServer(self):
+ if self.proxy_process:
+ logging.debug('Stopping Web-Page-Replay')
+ # Use a SIGINT here so that it can do graceful cleanup.
+ # Otherwise, we will leave subprocesses hanging.
+ self.proxy_process.send_signal(signal.SIGINT)
+ self.proxy_process.wait()
+ if self.log_fh:
+ self.log_fh.close()
+
+
+class ChromiumPaths(object):
+ """Collect all the path handling together."""
+ PATHS = {
+ 'archives': 'src/data/page_cycler/webpagereplay',
+ '.wpr': 'src/data/page_cycler/webpagereplay/{TEST_NAME}.wpr',
+ '.wpr_alt': 'src/tools/page_cycler/webpagereplay/tests/{TEST_NAME}.wpr',
+ 'start.html': 'src/tools/page_cycler/webpagereplay/start.html',
+ 'extension': 'src/tools/page_cycler/webpagereplay/extension',
+ 'logs': 'src/webpagereplay_logs/{TEST_EXE_NAME}',
+ 'replay': 'tools/build/third_party/webpagereplay',
+ }
+
+ def __init__(self, **replacements):
+ """Initialize ChromiumPaths.
+
+ Args:
+ replacements: a dict of format replacements for PATHS such as
+ {'TEST_NAME': '2012Q2', 'TEST_EXE_NAME': 'performance_ui_tests'}.
+ """
+ module_dir = os.path.dirname(__file__)
+ self.base_dir = os.path.abspath(os.path.join(
+ module_dir, '..', '..', '..', '..'))
+ self.replacements = replacements
+
+ def __getitem__(self, key):
+ path_parts = [x.format(**self.replacements)
+ for x in self.PATHS[key].split('/')]
+ return os.path.join(self.base_dir, *path_parts)
+
+
+def LaunchChromium(chrome_exe, chromium_paths, test_name,
+ is_dns_forwarded, use_auto):
+ """Launch chromium to run WPR-backed page cycler tests.
+
+ These options need to be kept in sync with
+ src/chrome/test/perf/page_cycler_test.cc.
+ """
+ REPLAY_HOST='127.0.0.1'
+ user_data_dir = USER_DATA_DIR.format(**{'TEMP': tempfile.gettempdir()})
+ chromium_args = [
+ chrome_exe,
+ '--load-extension=%s' % chromium_paths['extension'],
+ '--testing-fixed-http-port=%s' % HTTP_PORT,
+ '--testing-fixed-https-port=%s' % HTTPS_PORT,
+ '--disable-background-networking',
+ '--enable-experimental-extension-apis',
+ '--enable-file-cookies',
+ '--enable-logging',
+ '--log-level=0',
+ '--enable-stats-table',
+ '--enable-benchmarking',
+ '--ignore-certificate-errors',
+ '--metrics-recording-only',
+ '--activate-on-launch',
+ '--no-first-run',
+ '--no-proxy-server',
+ '--user-data-dir=%s' % user_data_dir,
+ '--window-size=1280,1024',
+ ]
+ if not is_dns_forwarded:
+ chromium_args.append('--host-resolver-rules=MAP * %s' % REPLAY_HOST)
+ start_url = 'file://%s?test=%s' % (chromium_paths['start.html'], test_name)
+ if use_auto:
+ start_url += '&auto=1'
+ chromium_args.append(start_url)
+ if os.path.exists(user_data_dir):
+ shutil.rmtree(user_data_dir)
+ os.makedirs(user_data_dir)
+ try:
+ logging.debug('Starting Chrome: %s', chromium_args)
+ retval = subprocess.call(chromium_args)
+ finally:
+ shutil.rmtree(user_data_dir)
+
+
+def main():
+ log_level = logging.DEBUG
+ logging.basicConfig(level=log_level,
+ format='%(asctime)s %(filename)s:%(lineno)-3d'
+ ' %(levelname)s %(message)s',
+ datefmt='%y%m%d %H:%M:%S')
+
+ option_parser = optparse.OptionParser(usage=USAGE)
+ option_parser.add_option(
+ '', '--auto', action='store_true', default=False,
+ help='Start test automatically.')
+ option_parser.add_option(
+ '', '--replay-dir', default=None,
+ help='Run replay from this directory instead of tools/build/third_party.')
+ replay_group = optparse.OptionGroup(option_parser,
+ 'Options for replay.py', 'These options are passed through to replay.py.')
+ replay_group.add_option(
+ '', '--record', action='store_true', default=False,
+ help='Record a new WPR archive.')
+ replay_group.add_option( # use default that does not require sudo
+ '', '--dns_forwarding', default=False, action='store_true',
+ help='Forward DNS requests to the local replay server.')
+ option_parser.add_option_group(replay_group)
+ options, args = option_parser.parse_args()
+ if len(args) != 2:
+ option_parser.error('Need CHROME_EXE and TEST_NAME.')
+ return 1
+ chrome_exe, test_name = args
+
+ if not os.path.exists(chrome_exe):
+ print >>sys.stderr, 'Chrome path does not exist:', chrome_exe
+ return 1
+
+ chromium_paths = ChromiumPaths(
+ TEST_NAME=test_name,
+ TEST_EXE_NAME='webpagereplay_utils')
+ if os.path.exists(chromium_paths['archives']):
+ archive_path = chromium_paths['.wpr']
+ else:
+ archive_path = chromium_paths['.wpr_alt']
+ if not os.path.exists(archive_path) and not options.record:
+ print >>sys.stderr, 'Archive does not exist:', archive_path
+ return 1
+
+ replay_options = []
+ if options.record:
+ replay_options.append('--record')
+ if not options.dns_forwarding:
+ replay_options.append('--no-dns_forwarding')
+
+ if options.replay_dir:
+ replay_dir = options.replay_dir
+ else:
+ replay_dir = chromium_paths['replay']
+ wpr = ReplayLauncher(replay_dir, archive_path,
+ chromium_paths['logs'], replay_options)
+ try:
+ wpr.StartServer()
+ LaunchChromium(chrome_exe, chromium_paths, test_name,
+ options.dns_forwarding, options.auto)
+ finally:
+ wpr.StopServer()
+ return 0
+
+if '__main__' == __name__:
+ sys.exit(main())