Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1466)

Unified Diff: chrome/browser/net/predictor_browsertest.cc

Issue 1881463003: Add a browsertest suite for net predictor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: mmenke@ review Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/net/predictor_browsertest.cc
diff --git a/chrome/browser/net/predictor_browsertest.cc b/chrome/browser/net/predictor_browsertest.cc
index 0b1884856d51aaa37ca2f5757b48499e2ff05839..5fe2eb38e550c550efbcc8b47a3f690b59e58313 100644
--- a/chrome/browser/net/predictor_browsertest.cc
+++ b/chrome/browser/net/predictor_browsertest.cc
@@ -16,11 +16,13 @@
#include "chrome/browser/net/predictor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
@@ -30,6 +32,11 @@
#include "net/socket/stream_socket.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/test/url_request/url_request_failed_job.h"
+#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_interceptor.h"
#include "testing/gmock/include/gmock/gmock.h"
using content::BrowserThread;
@@ -71,7 +78,11 @@ class ConnectionListener
// Get called from the EmbeddedTestServer thread to be notified that
// a connection was read from.
- void ReadFromSocket(const net::StreamSocket& connection) override {
+ void ReadFromSocket(const net::StreamSocket& connection, int rv) override {
+ // Don't log a read if no data was transferred. This case often happens if
+ // the sockets of the test server are being flushed and disconnected.
+ if (rv <= 0)
+ return;
base::AutoLock lock(lock_);
uint16_t socket = GetPort(connection);
EXPECT_FALSE(sockets_.find(socket) == sockets_.end());
@@ -101,6 +112,11 @@ class ConnectionListener
void WaitUntilFirstConnectionRead() { read_loop_.Run(); }
+ void ClearSockets() {
mmenke 2016/04/22 16:29:26 ClearSocketCounts? The external API is all about
Charlie Harrison 2016/04/28 16:29:51 Done.
+ base::AutoLock lock(lock_);
+ sockets_.clear();
+ }
+
private:
static uint16_t GetPort(const net::StreamSocket& connection) {
// Get the remote port of the peer, since the local port will always be the
@@ -202,6 +218,111 @@ class HostResolutionRequestRecorder : public net::HostResolverProc {
DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder);
};
+// This class intercepts URLRequests and responds with 404s.
+class NotFoundRequestInterceptor : public net::URLRequestInterceptor {
+ public:
+ NotFoundRequestInterceptor() {}
+ ~NotFoundRequestInterceptor() override {}
+
+ private:
+ net::URLRequestJob* MaybeInterceptRequest(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override {
+ const char kHeaders[] =
+ "HTTP/1.1 404 Not Found\n"
+ "\n";
+ std::string headers(kHeaders, arraysize(kHeaders));
mmenke 2016/04/22 16:29:25 Above two lines aren't used.
Charlie Harrison 2016/04/28 16:29:51 Done.
+
+ return new net::URLRequestFailedJob(request, network_delegate,
+ net::ERR_ABORTED);
+ }
+ DISALLOW_COPY_AND_ASSIGN(NotFoundRequestInterceptor);
+};
+
+class CrossSitePredictorObserver
+ : public chrome_browser_net::PredictorObserver {
+ public:
+ CrossSitePredictorObserver(const GURL source_host, const GURL cross_site_host)
mmenke 2016/04/22 16:29:25 nit: const GURL& (x2)
mmenke 2016/04/22 16:29:25 include gurl.h
Charlie Harrison 2016/04/28 16:29:51 Done.
Charlie Harrison 2016/04/28 16:29:51 Done.
+ : source_host_(source_host),
+ cross_site_host_(cross_site_host),
+ cross_site_learned_(0),
+ same_site_learned_(0),
mmenke 2016/04/22 16:29:25 I don't think we ever use same_site_learned_? The
Charlie Harrison 2016/04/28 16:29:51 Removed. If we want to add tests for them we can l
+ cross_site_preconnected_(0),
+ same_site_preconnected_(0) {}
+
+ void OnPreconnectUrl(
+ const GURL& original_url,
+ const GURL& first_party_for_cookies,
+ chrome_browser_net::UrlInfo::ResolutionMotivation motivation,
+ int count) override {
+ base::AutoLock lock(lock_);
+ if (original_url == cross_site_host_) {
+ cross_site_preconnected_ = std::max(cross_site_preconnected_, count);
+ } else if (original_url == source_host_) {
+ same_site_preconnected_ = std::max(same_site_preconnected_, count);
+ } else {
+ NOTREACHED() << "Preconnected" << original_url
mmenke 2016/04/22 16:29:25 Should use ADD_FAILURE() instead (A gtest macro th
Charlie Harrison 2016/04/28 16:29:51 Done.
+ << " When should only be preconnecting the source host: "
+ << source_host_
+ << " or the cross site host: " << cross_site_host_;
+ }
+ }
+
+ void OnLearnFromNavigation(const GURL& referring_url,
+ const GURL& target_url) override {
+ base::AutoLock lock(lock_);
+ if (referring_url == source_host_ && target_url == cross_site_host_) {
+ cross_site_learned_++;
+ } else if (referring_url == source_host_ && target_url == source_host_) {
+ same_site_learned_++;
+ } else if (referring_url != cross_site_host_) {
mmenke 2016/04/22 16:29:25 What if target_url is unexpected? Can we just do:
Charlie Harrison 2016/04/28 16:29:51 The code in your comment is a bit unclear but I th
+ NOTREACHED() << "Learned " << referring_url << " => " << target_url
mmenke 2016/04/22 16:29:25 ADD_FAILURE()
Charlie Harrison 2016/04/28 16:29:51 Done.
+ << " When should only be learning the source host: "
+ << source_host_
+ << " or the cross site host: " << cross_site_host_;
+ }
+ }
+
+ void ResetCounts() {
+ base::AutoLock lock(lock_);
+ cross_site_learned_ = 0;
+ same_site_learned_ = 0;
+ cross_site_preconnected_ = 0;
+ same_site_preconnected_ = 0;
+ }
+
+ int cross_site_learned() const {
+ base::AutoLock lock(lock_);
+ return cross_site_learned_;
+ }
+
+ int same_site_learned() const {
+ base::AutoLock lock(lock_);
+ return same_site_learned_;
+ }
+
+ int cross_site_preconnected() const {
+ base::AutoLock lock(lock_);
+ return cross_site_preconnected_;
+ }
+
+ int same_site_preconnected() const {
+ base::AutoLock lock(lock_);
+ return same_site_preconnected_;
+ }
+
+ private:
+ const GURL source_host_;
+ const GURL cross_site_host_;
+
+ int cross_site_learned_;
+ int same_site_learned_;
+
+ int cross_site_preconnected_;
+ int same_site_preconnected_;
+ mutable base::Lock lock_;
mmenke 2016/04/22 16:29:25 DISALLOW_COPY_AND_ASSIGN
Charlie Harrison 2016/04/28 16:29:51 Done.
+};
+
} // namespace
namespace chrome_browser_net {
@@ -212,8 +333,8 @@ class PredictorBrowserTest : public InProcessBrowserTest {
: startup_url_("http://host1:1"),
referring_url_("http://host2:1"),
target_url_("http://host3:1"),
- host_resolution_request_recorder_(new HostResolutionRequestRecorder) {
- }
+ host_resolution_request_recorder_(new HostResolutionRequestRecorder),
+ cross_site_test_server_(new net::EmbeddedTestServer()) {}
protected:
void SetUpInProcessBrowserTestFixture() override {
@@ -229,10 +350,43 @@ class PredictorBrowserTest : public InProcessBrowserTest {
switches::kEnableBlinkFeatures, kBlinkPreconnectFeature);
}
+ std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
mmenke 2016/04/22 16:29:25 This method name should be clearer, and may need a
Charlie Harrison 2016/04/28 16:29:52 Updated the name to "RedirectForPathPathHandler".
+ const net::test_server::HttpRequest& request) {
+ if (request.GetURL().path() != "/")
mmenke 2016/04/22 16:29:25 Suggest taking this as an argument, then can have
Charlie Harrison 2016/04/28 16:29:52 Done.
+ return nullptr;
+ std::unique_ptr<net::test_server::BasicHttpResponse> response(
+ new net::test_server::BasicHttpResponse);
+ response->set_code(net::HTTP_MOVED_PERMANENTLY);
+ response->AddCustomHeader(
+ "Location", cross_site_test_server()->GetURL("/title1.html").spec());
mmenke 2016/04/22 16:29:25 Calling into the cross_site_test_server on the oth
Charlie Harrison 2016/04/28 16:29:52 Done.
+ return std::move(response);
+ }
+
void SetUpOnMainThread() override {
+ embedded_test_server()->RegisterRequestHandler(base::Bind(
+ &PredictorBrowserTest::HandleRequest, base::Unretained(this)));
+ cross_site_test_server_->ServeFilesFromSourceDirectory("chrome/test/data/");
+
connection_listener_.reset(new ConnectionListener());
+ cross_site_connection_listener_.reset(new ConnectionListener());
embedded_test_server()->SetConnectionListener(connection_listener_.get());
+ cross_site_test_server_->SetConnectionListener(
+ cross_site_connection_listener_.get());
ASSERT_TRUE(embedded_test_server()->Start());
+ ASSERT_TRUE(cross_site_test_server_->Start());
+
+ predictor()->set_preconnect_enabled_for_test(true);
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&PredictorBrowserTest::SetUpOnIOThread,
+ base::Unretained(this)));
+ }
+
+ void SetUpOnIOThread() {
+ // Ignore all favicon requests. These are tricky to deal with because they
+ // are scheduled on the UI thread and are not accounted for in the load
+ // event. The interceptor will respond with 404s, and won't connect any
+ // sockets to the test server.
+ AddUrlInterceptor(cross_site_test_server()->GetURL("/favicon.ico"));
}
void TearDownOnMainThread() override {
@@ -254,29 +408,22 @@ class PredictorBrowserTest : public InProcessBrowserTest {
}
void LearnAboutInitialNavigation(const GURL& url) {
- Predictor* predictor = browser()->profile()->GetNetworkPredictor();
- BrowserThread::PostTask(BrowserThread::IO,
- FROM_HERE,
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&Predictor::LearnAboutInitialNavigation,
- base::Unretained(predictor),
- url));
+ base::Unretained(predictor()), url));
content::RunAllPendingInMessageLoop(BrowserThread::IO);
}
void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) {
- Predictor* predictor = browser()->profile()->GetNetworkPredictor();
- BrowserThread::PostTask(BrowserThread::IO,
- FROM_HERE,
- base::Bind(&Predictor::LearnFromNavigation,
- base::Unretained(predictor),
- referring_url,
- target_url));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&Predictor::LearnFromNavigation,
+ base::Unretained(predictor()), referring_url, target_url));
content::RunAllPendingInMessageLoop(BrowserThread::IO);
}
void PrepareFrameSubresources(const GURL& url) {
- Predictor* predictor = browser()->profile()->GetNetworkPredictor();
- predictor->PredictFrameSubresources(url, GURL());
+ predictor()->PredictFrameSubresources(url, GURL());
}
void GetListFromPrefsAsString(const char* list_path,
@@ -299,26 +446,431 @@ class PredictorBrowserTest : public InProcessBrowserTest {
return host_resolution_request_recorder_->RequestedHostnameCount();
}
+ net::EmbeddedTestServer* cross_site_test_server() {
+ return cross_site_test_server_.get();
+ }
+
+ Predictor* predictor() { return browser()->profile()->GetNetworkPredictor(); }
+
+ void InstallPredictorObserver() {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PredictorBrowserTest::InstallPredictorObserverOnIOThread,
+ base::Unretained(this), base::Unretained(predictor())));
+ }
+
+ void InstallPredictorObserverOnIOThread(
+ chrome_browser_net::Predictor* predictor) {
+ observer_.reset(
+ new CrossSitePredictorObserver(embedded_test_server()->base_url(),
+ cross_site_test_server()->base_url()));
mmenke 2016/04/22 16:29:25 Suggest just creating the observer_ in InstallPred
Charlie Harrison 2016/04/28 16:29:51 Done.
+ predictor->SetObserver(observer_.get());
+ }
+
+ CrossSitePredictorObserver* observer() { return observer_.get(); }
+
+ // Navigate to an html file and request async fetches. Wait until the fetches
mmenke 2016/04/22 16:29:25 Suggest "Navigate to an html file and request asyn
Charlie Harrison 2016/04/28 16:29:51 Done.
+ // complete before continuing. Note that "cors" here means using cors-mode in
+ // correspondence with the fetch spec. This translates to using the privacy
+ // mode socket pools.
+ void NavigateToCrossSiteHTMLURL(int num_cors, const char* file_suffix) {
+ const GURL& base_url = cross_site_test_server()->base_url();
+ std::string path = base::StringPrintf(
+ "/predictor/"
+ "predictor_cross_site%s.html?subresourceHost=%s&"
+ "numCORSResources=%d",
+ file_suffix, base_url.spec().c_str(), num_cors);
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL(path));
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(), "sendFetches()",
mmenke 2016/04/22 16:29:25 Maybe sendFetches -> startFetchesAndWaitForReply?
Charlie Harrison 2016/04/28 16:29:51 SGTM. Done.
+ &result));
+ EXPECT_TRUE(result);
+ }
+
+ // Simulate the target server restarting. Delete all sockets and reset members
+ // tracking preconnects.
+ void ResetTarget() {
+ cross_site_test_server()->FlushAllSocketsAndConnectionsOnUIThread();
+ cross_site_connection_listener_->ClearSockets();
+ EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ }
+
+ static void AddUrlInterceptor(const GURL url) {
mmenke 2016/04/22 16:29:25 GURL&
Charlie Harrison 2016/04/28 16:29:51 Done.
+ net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
+ url, make_scoped_ptr(new NotFoundRequestInterceptor()));
+ }
+
const GURL startup_url_;
const GURL referring_url_;
const GURL target_url_;
std::unique_ptr<ConnectionListener> connection_listener_;
+ std::unique_ptr<ConnectionListener> cross_site_connection_listener_;
private:
scoped_refptr<HostResolutionRequestRecorder>
host_resolution_request_recorder_;
std::unique_ptr<net::ScopedDefaultHostResolverProc>
scoped_host_resolver_proc_;
+ std::unique_ptr<net::EmbeddedTestServer> cross_site_test_server_;
+ std::unique_ptr<CrossSitePredictorObserver> observer_;
};
-IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) {
+// Test the html test harness used to initiate cross site fetches. These
+// initiate cross site subresource requests to the cross site test server.
+// Inspect the predictor's internal state to make sure that they are properly
+// logged.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteUseOneSocket) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */);
+ EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+}
+
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteUseThreeSockets) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(3 /* num_cors */, "" /* file_suffix */);
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(3, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+}
+
+// The following tests confirm that Chrome accurately predicts preconnects after
+// learning from navigations. Note that every "learned" connection adds ~.33 to
+// the expected connection number, which starts at 2. Every preconnect Chrome
+// performs multiplies the expected connections by .66. One additional
+// complexity
+// is that if a preconnect from A to B is learned, an extra preconnect will be
+// issued if the host part of A and B match (ignoring port).
+// TODO(csharrison): This logic could probably be removed.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteSimplePredictionAfterOneNavigation) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */);
+ EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(2, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm a preconnect. Note that because the two
+ // embedded test servers have the same host_piece, an extra preconnect is
+ // issued. This results in ceil(2.66) + 1 = 4 preconnects.
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// Expect that the predictor correctly predicts subframe navigations.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeCrossSitePrediction) {
+ InstallPredictorObserver();
+ ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL(
+ "/predictor/predictor_cross_site_subframe_nav.html"));
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ base::StringPrintf(
+ "navigateSubframe('%s')",
+ cross_site_test_server()->GetURL("/title1.html").spec().c_str()),
+ &result));
+ EXPECT_TRUE(result);
+ EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm a preconnect. Note that because the two
+ // embedded test servers have the same host_piece, an extra preconnect is
+ // issued. This results in ceil(2 + .33) + 1 = 4 preconnects.
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// Expect that the predictor correctly preconnects an already learned resource
+// if the host shows up in a subframe.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeInitiatesPreconnects) {
+ InstallPredictorObserver();
+ // Navigate to the normal cross site URL to learn the relationship.
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */);
+ EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm a preconnect. Note that because the two
+ // embedded test servers have the same host_piece, an extra preconnect is
+ // issued. This results in ceil(2 + .33) + 1 = 4 preconnects.
+ NavigateToDataURLWithContent(
+ "<iframe src=\"" + embedded_test_server()->GetURL("/title1.html").spec() +
+ "\"></iframe>");
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// Expect that the predictor correctly learns the subresources a subframe needs
+// to preconnect to.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeLearning) {
+ InstallPredictorObserver();
+ std::string path = base::StringPrintf(
+ "/predictor/"
+ "predictor_cross_site.html?subresourceHost=%s&"
+ "numCORSResources=1&sendImmediately=1",
+ cross_site_test_server()->base_url().spec().c_str());
+ NavigateToDataURLWithContent(
+ base::StringPrintf("<iframe src=\"%s\"></iframe>",
+ embedded_test_server()->GetURL(path).spec().c_str()));
+ cross_site_connection_listener_->WaitUntilFirstConnectionRead();
+ EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm a preconnect. Note that because the two
+ // embedded test servers have the same host_piece, an extra preconnect is
+ // issued. This results in ceil(2 + .33) + 1 = 4 preconnects.
+ NavigateToDataURLWithContent(
+ "<iframe src=\"" + embedded_test_server()->GetURL("/title1.html").spec() +
+ "\"></iframe>");
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// This tests the implementation details of the predictor. The current predictor
+// only learns via the referrer header.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteNoReferrerNoLearning) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */,
+ "_no_referrer" /* file_suffix */);
+ EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+}
+
+// This test navigates to an html file with a tag:
+// <meta name="referrer" content="never">
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteNoReferrerNoPredictionAfterOneNavigation) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(2 /* num_cors */,
+ "_no_referrer" /* file_suffix */);
+ EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm that no preconnects occurred.
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_preconnected());
+}
+
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteSimplePredictionAfterTwoNavigations) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */);
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */);
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // Navigate again and confirm a preconnect. Note that because the two
+ // embedded test servers have the same host_piece, an extra preconnect is
+ // issued. This results in ceil((2 + .33)*.66 + .33) + 1 ~= 3 preconnects.
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(3, observer()->cross_site_preconnected());
+}
+
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteSimplePredictionAfterTwoNavigations2) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */);
+ NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */);
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ // (((2 + .66)*.66) + .66) + 1 ~= 3.4
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// The first navigation uses a subresource. Subsequent navigations don't use
+// that subresource. This tests how the predictor forgets about these bad
+// navigations.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ForgetBadPrediction) {
+ InstallPredictorObserver();
+ NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */);
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ // (2 + .33) + 1 = 3.33.
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+
+ // ceil((2 + .33) * .66) + 1 = 3.
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(3, observer()->cross_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ // ceil((2 + .33) * .66 * .66) + 1 = 3.
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(3, observer()->cross_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ // Finally, (2 + .33) * .66^3 ~= .67. Not enough for a preconnect.
+ EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_preconnected());
+}
+
+// The predictor does not follow redirects if the original url had a non-empty
+// path (a path that was more than just "/").
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteRedirectNoPredictionWithPath) {
+ InstallPredictorObserver();
+ ui_test_utils::NavigateToURL(
+ browser(),
+ embedded_test_server()->GetURL(base::StringPrintf(
+ "/server-redirect?%s",
+ cross_site_test_server()->GetURL("/title1.html").spec().c_str())));
+ EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(0, observer()->cross_site_preconnected());
+}
+
+// The predictor does follow redirects if the original url had an empty path
+// (a path that was more than just "/"). Use the registered "/" path to redirect
+// to the target test server.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteRedirectPredictionWithNoPath) {
+ InstallPredictorObserver();
+ ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
+ EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ // Preconnect 4 sockets because ceil(2 + .33) + 1 = 4.
+ EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(4, observer()->cross_site_preconnected());
+}
+
+// This test uses "localhost" instead of "127.0.0.1" to avoid extra preconnects
+// to hosts with the same host piece (ignoring port). Note that the preconnect
+// observer is not used here due to its strict checks on hostname.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteRedirectPredictionWithNoPathDifferentHostName) {
+ GURL localhost_source = GURL(base::StringPrintf(
+ "http://localhost:%s/predictor/"
+ "predictor_cross_site.html?subresourceHost=%s&numCORSResources=1",
+ embedded_test_server()->base_url().port().c_str(),
+ cross_site_test_server()->base_url().spec().c_str()));
+ ui_test_utils::NavigateToURL(browser(), localhost_source);
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(), "sendFetches()",
+ &result));
+ EXPECT_TRUE(result);
+
+ EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount());
+
+ ResetTarget();
+
+ ui_test_utils::NavigateToURL(
+ browser(), GURL(base::StringPrintf(
+ "http://localhost:%s/title1.html",
+ embedded_test_server()->base_url().port().c_str())));
+ // Preconnect 3 sockets because ceil(2 + .33) = 3. Note that this time there
+ // is no additional connection due to same host.
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+}
+
+// Perform the "/" redirect twice and make sure the predictor updates twice.
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ CrossSiteTwoRedirectsPredictionWithNoPath) {
+ InstallPredictorObserver();
+ ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
+ EXPECT_EQ(1, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
+ EXPECT_EQ(2, observer()->cross_site_learned());
+ EXPECT_EQ(2, observer()->same_site_preconnected());
+
+ ResetTarget();
+ observer()->ResetCounts();
+
+ ui_test_utils::NavigateToURL(browser(),
+ embedded_test_server()->GetURL("/title1.html"));
+ // 3 preconnects expected because (2 + .33) * .66 + .33 + 1 ~= 2.87.
+ EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount());
+ EXPECT_EQ(3, observer()->cross_site_preconnected());
+}
+
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest,
+ PRE_ShutdownStartupCyclePreresolve) {
// Prepare state that will be serialized on this shut-down and read on next
- // start-up.
+ // start-up. Ensure preresolution over preconnection.
LearnAboutInitialNavigation(startup_url_);
+ // The target url will have a expected connection count of 2 after this call.
LearnFromNavigation(referring_url_, target_url_);
+
+ // In order to reduce the expected connection count < .8, issue predictions 3
+ // times. 2 * .66^3 ~= .58.
+ PrepareFrameSubresources(referring_url_);
+ PrepareFrameSubresources(referring_url_);
+ PrepareFrameSubresources(referring_url_);
}
-IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) {
+IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCyclePreresolve) {
// Make sure that the Preferences file is actually wiped of all DNS prefetch
// related data after start-up.
std::string cleared_startup_list;

Powered by Google App Engine
This is Rietveld 408576698