Chromium Code Reviews| Index: chrome/browser/net/predictor_browsertest.cc |
| diff --git a/chrome/browser/net/predictor_browsertest.cc b/chrome/browser/net/predictor_browsertest.cc |
| index be8568ae32814a97956936b9c6ac16cc3cb4b7b7..cc4718f7c3d664ba83f09894a0fa2c3292c1c9bf 100644 |
| --- a/chrome/browser/net/predictor_browsertest.cc |
| +++ b/chrome/browser/net/predictor_browsertest.cc |
| @@ -7,6 +7,7 @@ |
| #include <algorithm> |
| #include <memory> |
| +#include <set> |
| #include "base/base64.h" |
| #include "base/bind.h" |
| @@ -83,12 +84,14 @@ std::unique_ptr<net::test_server::HttpResponse> RedirectForPathHandler( |
| } |
| const char kBlinkPreconnectFeature[] = "LinkPreconnect"; |
| -const char kChromiumHostname[] = "chromium.org"; |
| -const char kInvalidLongHostname[] = "illegally-long-hostname-over-255-" |
| - "characters-should-not-send-an-ipc-message-to-the-browser-" |
| - "0000000000000000000000000000000000000000000000000000000000000000000000000" |
| - "0000000000000000000000000000000000000000000000000000000000000000000000000" |
| - "000000000000000000000000000000000000000000000000000000.org"; |
| +const char kChromiumHost[] = "http://chromium.org"; |
|
eroman
2016/05/27 00:56:38
nit: Can you call this a "url" or "origin" now?
Charlie Harrison
2016/05/27 11:55:53
Done.
|
| +const char kInvalidLongHost[] = |
|
eroman
2016/05/27 00:56:38
same
Charlie Harrison
2016/05/27 11:55:53
Done.
|
| + "http://" |
| + "illegally-long-hostname-over-255-characters-should-not-send-an-ipc-" |
| + "message-to-the-browser-" |
| + "00000000000000000000000000000000000000000000000000000000000000000000000000" |
| + "00000000000000000000000000000000000000000000000000000000000000000000000000" |
| + "0000000000000000000000000000000000000000000000000000.org"; |
| // Gets notified by the EmbeddedTestServer on incoming connections being |
| // accepted or read from, keeps track of them and exposes that info to |
| @@ -245,78 +248,6 @@ class ConnectionListener |
| DISALLOW_COPY_AND_ASSIGN(ConnectionListener); |
| }; |
| -// Records a history of all hostnames for which resolving has been requested, |
| -// and immediately fails the resolution requests themselves. |
| -class HostResolutionRequestRecorder : public net::HostResolverProc { |
| - public: |
| - HostResolutionRequestRecorder() |
| - : HostResolverProc(NULL), |
| - is_waiting_for_hostname_(false) { |
| - } |
| - |
| - int Resolve(const std::string& host, |
| - net::AddressFamily address_family, |
| - net::HostResolverFlags host_resolver_flags, |
| - net::AddressList* addrlist, |
| - int* os_error) override { |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, |
| - FROM_HERE, |
| - base::Bind(&HostResolutionRequestRecorder::AddToHistory, |
| - base::Unretained(this), |
| - host)); |
| - return net::ERR_NAME_NOT_RESOLVED; |
| - } |
| - |
| - int RequestedHostnameCount() const { |
| - return requested_hostnames_.size(); |
| - } |
| - |
| - bool HasHostBeenRequested(const std::string& hostname) const { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - return std::find(requested_hostnames_.begin(), |
| - requested_hostnames_.end(), |
| - hostname) != requested_hostnames_.end(); |
| - } |
| - |
| - void WaitUntilHostHasBeenRequested(const std::string& hostname) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - DCHECK(!is_waiting_for_hostname_); |
| - if (HasHostBeenRequested(hostname)) |
| - return; |
| - waiting_for_hostname_ = hostname; |
| - is_waiting_for_hostname_ = true; |
| - content::RunMessageLoop(); |
| - } |
| - |
| - private: |
| - ~HostResolutionRequestRecorder() override {} |
| - |
| - void AddToHistory(const std::string& hostname) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - requested_hostnames_.push_back(hostname); |
| - if (is_waiting_for_hostname_ && waiting_for_hostname_ == hostname) { |
| - is_waiting_for_hostname_ = false; |
| - waiting_for_hostname_.clear(); |
| - base::MessageLoop::current()->QuitWhenIdle(); |
| - } |
| - } |
| - |
| - // The hostname which WaitUntilHostHasBeenRequested is currently waiting for |
| - // to be requested. |
| - std::string waiting_for_hostname_; |
| - |
| - // Whether WaitUntilHostHasBeenRequested is waiting for a hostname to be |
| - // requested and thus is running a nested message loop. |
| - bool is_waiting_for_hostname_; |
| - |
| - // A list of hostnames for which resolution has already been requested. Only |
| - // to be accessed from the UI thread. |
| - std::vector<std::string> requested_hostnames_; |
| - |
| - DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder); |
| -}; |
| - |
| // This class intercepts URLRequests and responds with the URLRequestJob* |
| // callback provided by the constructor. Note that the port of the URL must |
| // match the port given in the constructor. |
| @@ -362,7 +293,8 @@ class CrossSitePredictorObserver |
| cross_site_host_(cross_site_host), |
| cross_site_learned_(0), |
| cross_site_preconnected_(0), |
| - same_site_preconnected_(0) {} |
| + same_site_preconnected_(0), |
| + dns_run_loop_(nullptr) {} |
| void OnPreconnectUrl( |
| const GURL& original_url, |
| @@ -402,6 +334,16 @@ class CrossSitePredictorObserver |
| } |
| } |
| + void OnDnsLookupFinished(const GURL& url, bool found) override { |
| + base::AutoLock lock(lock_); |
| + if (found) { |
| + successful_dns_lookups_.insert(url); |
| + } else { |
| + unsuccessful_dns_lookups_.insert(url); |
| + } |
| + CheckForWaitingLoop(); |
| + } |
| + |
| void ResetCounts() { |
| base::AutoLock lock(lock_); |
| cross_site_learned_ = 0; |
| @@ -424,10 +366,72 @@ class CrossSitePredictorObserver |
| return same_site_preconnected_; |
| } |
| + // Spins a run loop until |url| is added to one of the lookup maps. |
| + void WaitUntilHostLookedUp(const GURL& url) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + base::RunLoop run_loop; |
| + { |
| + base::AutoLock lock(lock_); |
| + DCHECK(waiting_on_dns_.is_empty()); |
| + DCHECK(!dns_run_loop_); |
| + waiting_on_dns_ = url; |
| + dns_run_loop_ = &run_loop; |
| + CheckForWaitingLoop(); |
| + } |
| + run_loop.Run(); |
| + } |
| + |
| + bool HasHostBeenLookedUpLocked(const GURL& url) { |
| + lock_.AssertAcquired(); |
| + return successful_dns_lookups_.find(url) != successful_dns_lookups_.end() || |
|
eroman
2016/05/27 00:56:38
optional: can use ContainsKey() to simplify the va
Charlie Harrison
2016/05/27 11:55:52
Done.
|
| + unsuccessful_dns_lookups_.find(url) != |
| + unsuccessful_dns_lookups_.end(); |
| + } |
| + |
| + bool HasHostBeenLookedUp(const GURL& url) { |
| + base::AutoLock lock(lock_); |
| + return HasHostBeenLookedUpLocked(url); |
| + } |
| + |
| + void CheckForWaitingLoop() { |
| + lock_.AssertAcquired(); |
| + if (waiting_on_dns_.is_empty()) |
| + return; |
| + if (!HasHostBeenLookedUpLocked(waiting_on_dns_)) |
| + return; |
| + DCHECK(dns_run_loop_); |
| + DCHECK(task_runner_); |
| + waiting_on_dns_ = GURL(); |
| + task_runner_->PostTask(FROM_HERE, dns_run_loop_->QuitClosure()); |
| + dns_run_loop_ = nullptr; |
| + } |
| + |
| + size_t TotalHostsLookedUp() { |
| + base::AutoLock lock(lock_); |
| + return successful_dns_lookups_.size() + unsuccessful_dns_lookups_.size(); |
| + } |
| + |
| + // Note: this method expects the URL to have been looked up. |
| + bool HostFound(const GURL& url) { |
| + base::AutoLock lock(lock_); |
| + EXPECT_TRUE(HasHostBeenLookedUpLocked(url)) << "Expected to have looked up" |
|
eroman
2016/05/27 00:56:38
nit: add a space at the end of the string.
Charlie Harrison
2016/05/27 11:55:53
Done.
|
| + << url.spec(); |
| + return successful_dns_lookups_.find(url) != successful_dns_lookups_.end(); |
| + } |
| + |
| + void set_task_runner( |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| + task_runner_.swap(task_runner); |
| + } |
| + |
| private: |
| const GURL source_host_; |
| const GURL cross_site_host_; |
| + GURL waiting_on_dns_; |
| + |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| + |
| // Protects all following members. They are read and updated from different |
| // threads. |
| base::Lock lock_; |
| @@ -436,6 +440,10 @@ class CrossSitePredictorObserver |
| int cross_site_preconnected_; |
| int same_site_preconnected_; |
| + std::set<GURL> successful_dns_lookups_; |
| + std::set<GURL> unsuccessful_dns_lookups_; |
| + base::RunLoop* dns_run_loop_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(CrossSitePredictorObserver); |
| }; |
| @@ -446,16 +454,27 @@ namespace chrome_browser_net { |
| class PredictorBrowserTest : public InProcessBrowserTest { |
| public: |
| PredictorBrowserTest() |
| - : startup_url_("http://host1:1"), |
| - referring_url_("http://host2:1"), |
| - target_url_("http://host3:1"), |
| - host_resolution_request_recorder_(new HostResolutionRequestRecorder), |
| - cross_site_test_server_(new net::EmbeddedTestServer()) {} |
| + : startup_url_("http://host1/"), |
| + referring_url_("http://host2/"), |
| + target_url_("http://host3/"), |
| + rule_based_resolver_proc_(new net::RuleBasedHostResolverProc(nullptr)), |
| + cross_site_test_server_(new net::EmbeddedTestServer()) { |
| + rule_based_resolver_proc_->AddRuleWithLatency("www.example.test", |
| + "127.0.0.1", 50); |
| + rule_based_resolver_proc_->AddRuleWithLatency("gmail.google.com", |
| + "127.0.0.1", 70); |
| + rule_based_resolver_proc_->AddRuleWithLatency("mail.google.com", |
| + "127.0.0.1", 44); |
| + rule_based_resolver_proc_->AddRuleWithLatency("gmail.com", "127.0.0.1", 63); |
| + rule_based_resolver_proc_->AddSimulatedFailure("*.notfound"); |
| + rule_based_resolver_proc_->AddRuleWithLatency("delay.google.com", |
| + "127.0.0.1", 1000 * 60); |
| + } |
| protected: |
| void SetUpInProcessBrowserTestFixture() override { |
| scoped_host_resolver_proc_.reset(new net::ScopedDefaultHostResolverProc( |
| - host_resolution_request_recorder_.get())); |
| + rule_based_resolver_proc_.get())); |
| InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); |
| } |
| @@ -466,6 +485,8 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| switches::kEnableBlinkFeatures, kBlinkPreconnectFeature); |
| command_line->AppendSwitchASCII(switches::kEnableFeatures, |
| "PreconnectMore"); |
| + command_line->AppendSwitchASCII(switches::kEnableFeatures, |
| + "UsePredictorDNSQueue"); |
| } |
| void SetUpOnMainThread() override { |
| @@ -488,6 +509,7 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| predictor()->SetPreconnectEnabledForTest(true); |
| InstallPredictorObserver(embedded_test_server()->base_url(), |
| cross_site_test_server()->base_url()); |
| + observer()->set_task_runner(task_runner_); |
| StartInterceptingCrossSiteOnUI(); |
| } |
| @@ -573,16 +595,24 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| serializer.Serialize(*list_value); |
| } |
| - bool HasHostBeenRequested(const std::string& hostname) const { |
| - return host_resolution_request_recorder_->HasHostBeenRequested(hostname); |
| + void WaitUntilHostsLookedUp(const network_hints::UrlList& names) { |
| + for (const GURL& url : names) |
| + observer()->WaitUntilHostLookedUp(url); |
| } |
| - void WaitUntilHostHasBeenRequested(const std::string& hostname) { |
| - host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname); |
| + void FloodResolveRequestsOnUIThread(const network_hints::UrlList& names) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::FloodResolveRequests, this, names)); |
| } |
| - int RequestedHostnameCount() const { |
| - return host_resolution_request_recorder_->RequestedHostnameCount(); |
| + void FloodResolveRequests(const network_hints::UrlList& names) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + for (int i = 0; i < 10; i++) { |
| + predictor()->DnsPrefetchMotivatedList(names, |
| + UrlInfo::PAGE_SCAN_MOTIVATED); |
| + } |
| } |
| net::EmbeddedTestServer* cross_site_test_server() { |
| @@ -639,6 +669,56 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| EXPECT_TRUE(test_server->FlushAllSocketsAndConnectionsOnUIThread()); |
| } |
| + // Note this method also expects that all the urls (found or not) were looked |
| + // up. |
| + void ExpectFoundUrls( |
| + const network_hints::UrlList& found_names, |
| + const network_hints::UrlList& not_found_names) { |
| + for (const auto& name : found_names) { |
| + EXPECT_TRUE(observer()->HostFound(name)) << "Expected to have found " |
| + << name.spec(); |
| + } |
| + for (const auto& name : not_found_names) { |
| + EXPECT_FALSE(observer()->HostFound(name)) << "Did not expect to find " |
| + << name.spec(); |
| + } |
| + } |
| + |
| + // This method verifies that |url| is in the predictors |results_| map. This |
|
eroman
2016/05/27 00:56:38
nit: predictors --> predictor's
Charlie Harrison
2016/05/27 11:55:52
Done.
|
| + // is used for pending lookups, and lookups performed before the observer is |
| + // attached. |
| + void ExpectUrlRequestedFromPredictorOnUIThread(const GURL& url) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::ExpectUrlRequestedFromPredictor, |
| + base::Unretained(this), url)); |
| + } |
| + |
| + void ExpectUrlRequestedFromPredictor(const GURL& url) { |
| + EXPECT_NE(predictor()->results_.find(url), predictor()->results_.end()); |
| + } |
| + |
| + void DiscardAllResultsOnUIThread() { |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&Predictor::DiscardAllResults, |
| + base::Unretained(predictor()))); |
| + } |
| + |
| + void ExpectValidPeakPendingLookupsOnUI(size_t num_names_requested) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::ExpectValidPeakPendingLookups, |
| + base::Unretained(this), num_names_requested)); |
| + } |
| + |
| + void ExpectValidPeakPendingLookups(size_t num_names_requested) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + EXPECT_LE(predictor()->peak_pending_lookups_, num_names_requested); |
| + EXPECT_LE(predictor()->peak_pending_lookups_, |
| + predictor()->max_concurrent_dns_lookups()); |
| + } |
| + |
| CrossSitePredictorObserver* observer() { return observer_.get(); } |
| // Navigate to an html file on embedded_test_server and tell it to request |
| @@ -669,8 +749,7 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| std::unique_ptr<ConnectionListener> cross_site_connection_listener_; |
| private: |
| - scoped_refptr<HostResolutionRequestRecorder> |
| - host_resolution_request_recorder_; |
| + scoped_refptr<net::RuleBasedHostResolverProc> rule_based_resolver_proc_; |
| std::unique_ptr<net::ScopedDefaultHostResolverProc> |
| scoped_host_resolver_proc_; |
| std::unique_ptr<net::EmbeddedTestServer> cross_site_test_server_; |
| @@ -678,6 +757,66 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| }; |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SingleLookupTest) { |
| + DiscardAllResultsOnUIThread(); |
| + GURL goog("http://www.example.test/"); |
|
eroman
2016/05/27 00:56:38
probably better to call this "url" -- not actually
Charlie Harrison
2016/05/27 11:55:53
Done.
|
| + |
| + // Try to flood the predictor with many concurrent requests. |
| + network_hints::UrlList names{goog}; |
| + FloodResolveRequestsOnUIThread(names); |
| + observer()->WaitUntilHostLookedUp(goog); |
| + EXPECT_TRUE(observer()->HostFound(goog)); |
| + ExpectValidPeakPendingLookupsOnUI(1u); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ConcurrentLookupTest) { |
| + DiscardAllResultsOnUIThread(); |
| + GURL goog("http://www.example.test"), goog2("http://gmail.google.com"), |
|
eroman
2016/05/27 00:56:38
same here -- at least for one of them.
Charlie Harrison
2016/05/27 11:55:53
Done.
|
| + goog3("http://mail.google.com"), goog4("http://gmail.com"); |
| + GURL bad1("http://bad1.notfound"), bad2("http://bad2.notfound"); |
| + |
| + UrlList found_names{goog, goog3, goog2, goog4, goog}; |
| + UrlList not_found_names{bad1, bad2}; |
| + FloodResolveRequestsOnUIThread(found_names); |
| + FloodResolveRequestsOnUIThread(not_found_names); |
| + |
| + WaitUntilHostsLookedUp(found_names); |
| + WaitUntilHostsLookedUp(not_found_names); |
| + ExpectFoundUrls(found_names, not_found_names); |
| + ExpectValidPeakPendingLookupsOnUI(found_names.size() + |
| + not_found_names.size()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, MassiveConcurrentLookupTest) { |
| + DiscardAllResultsOnUIThread(); |
| + UrlList not_found_names; |
| + for (int i = 0; i < 100; i++) { |
| + not_found_names.push_back( |
| + GURL(base::StringPrintf("http://host%d.notfound:80", i))); |
| + } |
| + FloodResolveRequestsOnUIThread(not_found_names); |
| + |
| + WaitUntilHostsLookedUp(not_found_names); |
| + ExpectFoundUrls(network_hints::UrlList(), not_found_names); |
| + ExpectValidPeakPendingLookupsOnUI(not_found_names.size()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + ShutdownWhenResolutionIsPendingTest) { |
| + GURL delayed_url("http://delay.google.com:80"); |
| + UrlList names{delayed_url}; |
| + |
| + // Flood with delayed requests, then wait. |
| + FloodResolveRequestsOnUIThread(names); |
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| + FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| + base::TimeDelta::FromMilliseconds(500)); |
| + base::MessageLoop::current()->Run(); |
| + |
| + ExpectUrlRequestedFromPredictor(delayed_url); |
| + EXPECT_FALSE(observer()->HasHostBeenLookedUp(delayed_url)); |
| +} |
| + |
| IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SimplePreconnectOne) { |
| predictor()->PreconnectUrl( |
| embedded_test_server()->base_url(), GURL(), |
| @@ -1128,24 +1267,28 @@ IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCyclePreresolve) { |
| // But also make sure this data has been first loaded into the Predictor, by |
| // inspecting that the Predictor starts making the expected hostname requests. |
| PrepareFrameSubresources(referring_url_); |
| - WaitUntilHostHasBeenRequested(startup_url_.host()); |
| - WaitUntilHostHasBeenRequested(target_url_.host()); |
| + observer()->WaitUntilHostLookedUp(target_url_); |
| + |
| + // Verify that both urls were requested by the predictor. Note that the |
| + // startup URL may be requested before the observer attaches itself. |
| + ExpectUrlRequestedFromPredictor(startup_url_); |
| + EXPECT_FALSE(observer()->HostFound(target_url_)); |
| } |
| -// Flaky on Windows: http://crbug.com/469120 |
| -#if defined(OS_WIN) |
| -#define MAYBE_DnsPrefetch DISABLED_DnsPrefetch |
| -#else |
| -#define MAYBE_DnsPrefetch DnsPrefetch |
| -#endif |
| -IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, MAYBE_DnsPrefetch) { |
| - int hostnames_requested_before_load = RequestedHostnameCount(); |
| - ui_test_utils::NavigateToURL( |
| - browser(), |
| - GURL(embedded_test_server()->GetURL("/predictor/dns_prefetch.html"))); |
| - WaitUntilHostHasBeenRequested(kChromiumHostname); |
| - ASSERT_FALSE(HasHostBeenRequested(kInvalidLongHostname)); |
| - ASSERT_EQ(hostnames_requested_before_load + 1, RequestedHostnameCount()); |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, DnsPrefetch) { |
| + // Navigate once to make sure all initial hostnames are requested. |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + |
| + size_t hosts_looked_up_before_load = observer()->TotalHostsLookedUp(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL( |
| + "/predictor/dns_prefetch.html")); |
| + observer()->WaitUntilHostLookedUp(GURL(kChromiumHost)); |
| + ASSERT_FALSE(observer()->HasHostBeenLookedUp(GURL(kInvalidLongHost))); |
| + |
| + EXPECT_FALSE(observer()->HostFound(GURL(kChromiumHost))); |
| + ASSERT_EQ(hosts_looked_up_before_load + 1, observer()->TotalHostsLookedUp()); |
| } |
| // Tests that preconnect warms up a socket connection to a test server. |