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..712d5b418faa18f446b1eb6ef78ba7cc3a1e0674 100644 |
| --- a/chrome/browser/net/predictor_browsertest.cc |
| +++ b/chrome/browser/net/predictor_browsertest.cc |
| @@ -249,56 +249,79 @@ class ConnectionListener |
| // and immediately fails the resolution requests themselves. |
| class HostResolutionRequestRecorder : public net::HostResolverProc { |
| public: |
| - HostResolutionRequestRecorder() |
| - : HostResolverProc(NULL), |
| - is_waiting_for_hostname_(false) { |
| - } |
| + explicit HostResolutionRequestRecorder(net::HostResolverProc* previous) |
| + : HostResolverProc(previous), run_loop_(nullptr) {} |
| int Resolve(const std::string& host, |
| net::AddressFamily address_family, |
| net::HostResolverFlags host_resolver_flags, |
| net::AddressList* addrlist, |
| int* os_error) override { |
| + // Note the rule based proc sleep synchronously. |
|
Randy Smith (Not in Mondays)
2016/05/23 19:55:18
Sorry, I'm getting past the lexing phase, but fail
Charlie Harrison
2016/05/24 16:57:06
Hahaha yeah that's extremely confusing. I meant th
|
| + int result = ResolveUsingPrevious(host, address_family, host_resolver_flags, |
| + addrlist, os_error); |
| BrowserThread::PostTask( |
| - BrowserThread::UI, |
| - FROM_HERE, |
| + BrowserThread::IO, FROM_HERE, |
| base::Bind(&HostResolutionRequestRecorder::AddToHistory, |
| - base::Unretained(this), |
| - host)); |
| - return net::ERR_NAME_NOT_RESOLVED; |
| + base::Unretained(this), host)); |
| + return result; |
| } |
| int RequestedHostnameCount() const { |
| + base::AutoLock lock(lock_); |
| return requested_hostnames_.size(); |
| } |
| bool HasHostBeenRequested(const std::string& hostname) const { |
| + base::AutoLock lock(lock_); |
| + return HasHostBeenRequestedLocked(hostname); |
| + } |
| + |
| + bool HasHostBeenRequestedLocked(const std::string& hostname) const { |
| + lock_.AssertAcquired(); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return std::find(requested_hostnames_.begin(), |
| requested_hostnames_.end(), |
| hostname) != requested_hostnames_.end(); |
| } |
| + void WaitUntilHostNamesHaveBeenRequested( |
| + const network_hints::UrlList& names) { |
| + for (const auto& name : names) { |
| + WaitUntilHostHasBeenRequested(name.host()); |
| + } |
| + } |
| + |
| 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(); |
| + base::RunLoop run_loop; |
| + { |
| + base::AutoLock lock(lock_); |
| + if (HasHostBeenRequestedLocked(hostname)) |
| + return; |
| + waiting_for_hostname_ = hostname; |
| + DCHECK(!run_loop_); |
| + run_loop_ = &run_loop; |
| + } |
| + run_loop_->Run(); |
| + } |
| + |
| + void set_task_runner( |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| + task_runner_.swap(task_runner); |
| } |
| private: |
| ~HostResolutionRequestRecorder() override {} |
| void AddToHistory(const std::string& hostname) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + base::AutoLock lock(lock_); |
| requested_hostnames_.push_back(hostname); |
| - if (is_waiting_for_hostname_ && waiting_for_hostname_ == hostname) { |
| - is_waiting_for_hostname_ = false; |
| + if (run_loop_ && waiting_for_hostname_ == hostname) { |
| waiting_for_hostname_.clear(); |
| - base::MessageLoop::current()->QuitWhenIdle(); |
| + task_runner_->PostTask(FROM_HERE, run_loop_->QuitClosure()); |
| + run_loop_ = nullptr; |
| } |
| } |
| @@ -306,14 +329,17 @@ class HostResolutionRequestRecorder : public net::HostResolverProc { |
| // 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_; |
| + base::RunLoop* run_loop_; |
| + |
| + // Protects |waiting_for_hostname_| and |requested_hostnames_|. |
| + mutable base::Lock lock_; |
| + |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder); |
| }; |
| @@ -449,8 +475,21 @@ 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), |
| - cross_site_test_server_(new net::EmbeddedTestServer()) {} |
| + rule_based_resolver_proc_(new net::RuleBasedHostResolverProc(nullptr)), |
| + host_resolution_request_recorder_( |
| + new HostResolutionRequestRecorder(rule_based_resolver_proc_.get())), |
| + 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 { |
| @@ -466,11 +505,14 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| switches::kEnableBlinkFeatures, kBlinkPreconnectFeature); |
| command_line->AppendSwitchASCII(switches::kEnableFeatures, |
| "PreconnectMore"); |
| + command_line->AppendSwitchASCII(switches::kEnableFeatures, |
| + "UsePredictorDNSQueue"); |
| } |
| void SetUpOnMainThread() override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| + host_resolution_request_recorder_->set_task_runner(task_runner_); |
| cross_site_test_server_->ServeFilesFromSourceDirectory("chrome/test/data/"); |
| connection_listener_.reset(new ConnectionListener()); |
| @@ -581,10 +623,31 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname); |
| } |
| + void WaitUntilHostNamesHaveBeenRequested( |
| + const network_hints::UrlList& names) { |
| + host_resolution_request_recorder_->WaitUntilHostNamesHaveBeenRequested( |
| + names); |
| + } |
| + |
| int RequestedHostnameCount() const { |
| return host_resolution_request_recorder_->RequestedHostnameCount(); |
| } |
| + void FloodResolveRequestsOnUIThread(const network_hints::UrlList& names) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::FloodResolveRequests, this, names)); |
| + } |
| + |
| + 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() { |
| return cross_site_test_server_.get(); |
| } |
| @@ -639,6 +702,31 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| EXPECT_TRUE(test_server->FlushAllSocketsAndConnectionsOnUIThread()); |
| } |
| + void ExpectFoundUrlsOnUIThread( |
| + const network_hints::UrlList& found_names, |
| + const network_hints::UrlList& not_found_names) { |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::ExpectFoundUrls, |
| + this, found_names, not_found_names)); |
| + } |
| + void ExpectFoundUrls(const network_hints::UrlList& found_names, |
| + const network_hints::UrlList& not_found_names) { |
| + for (const auto& name : found_names) { |
| + EXPECT_TRUE(predictor()->WasFound(name)) << "Expected to have found " |
| + << name.spec(); |
| + } |
| + for (const auto& name : not_found_names) { |
| + EXPECT_FALSE(predictor()->WasFound(name)) << "Did not expect to find " |
| + << name.spec(); |
| + } |
| + } |
| + |
| + void DiscardAllResultsOnUIThread() { |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&Predictor::DiscardAllResults, |
| + base::Unretained(predictor()))); |
| + } |
| + |
| CrossSitePredictorObserver* observer() { return observer_.get(); } |
| // Navigate to an html file on embedded_test_server and tell it to request |
| @@ -669,6 +757,7 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| std::unique_ptr<ConnectionListener> cross_site_connection_listener_; |
| private: |
| + scoped_refptr<net::RuleBasedHostResolverProc> rule_based_resolver_proc_; |
| scoped_refptr<HostResolutionRequestRecorder> |
| host_resolution_request_recorder_; |
| std::unique_ptr<net::ScopedDefaultHostResolverProc> |
| @@ -678,6 +767,84 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| }; |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SingleLookupTest) { |
| + DiscardAllResultsOnUIThread(); |
| + |
| + GURL goog("http://www.example.test:80"); |
| + |
| + network_hints::UrlList names{goog}; |
| + |
| + // Try to flood the predictor with many concurrent requests. |
| + FloodResolveRequestsOnUIThread(names); |
| + WaitUntilHostNamesHaveBeenRequested(names); |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + ExpectFoundUrlsOnUIThread(names, network_hints::UrlList()); |
| + |
| + EXPECT_GT(predictor()->peak_pending_lookups(), names.size() / 2); |
| + EXPECT_LE(predictor()->peak_pending_lookups(), names.size()); |
| + EXPECT_LE(predictor()->peak_pending_lookups(), |
| + predictor()->max_concurrent_dns_lookups()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ConcurrentLookupTest) { |
| + DiscardAllResultsOnUIThread(); |
| + |
| + GURL goog("http://www.example.test:80"), goog2("http://gmail.google.com:80"), |
| + goog3("http://mail.google.com:80"), goog4("http://gmail.com:80"); |
| + GURL bad1("http://bad1.notfound:80"), bad2("http://bad2.notfound:80"); |
| + |
| + UrlList found_names{goog, goog3, goog2, goog4, goog}; |
| + UrlList not_found_names{bad1, bad2}; |
| + |
| + FloodResolveRequestsOnUIThread(found_names); |
| + FloodResolveRequestsOnUIThread(not_found_names); |
| + WaitUntilHostNamesHaveBeenRequested(found_names); |
| + WaitUntilHostNamesHaveBeenRequested(not_found_names); |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + |
| + ExpectFoundUrlsOnUIThread(found_names, not_found_names); |
| + |
| + EXPECT_LE(predictor()->peak_pending_lookups(), |
| + found_names.size() + not_found_names.size()); |
| + EXPECT_LE(predictor()->peak_pending_lookups(), |
| + predictor()->max_concurrent_dns_lookups()); |
| +} |
| + |
| +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); |
| + WaitUntilHostNamesHaveBeenRequested(not_found_names); |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + |
| + ExpectFoundUrlsOnUIThread(network_hints::UrlList(), not_found_names); |
| + |
| + EXPECT_LE(predictor()->peak_pending_lookups(), not_found_names.size()); |
| + |
| + EXPECT_LE(predictor()->peak_pending_lookups(), |
| + predictor()->max_concurrent_dns_lookups()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + ShutdownWhenResolutionIsPendingTest) { |
| + GURL localhost("http://delay.google.com:80"); |
| + UrlList names; |
| + names.push_back(localhost); |
| + |
| + // Flood with delayed requests. |
| + FloodResolveRequestsOnUIThread(names); |
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| + FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| + base::TimeDelta::FromMilliseconds(500)); |
| + base::MessageLoop::current()->Run(); |
| + |
| + EXPECT_FALSE(predictor()->WasFound(localhost)); |
| +} |
| + |
| IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SimplePreconnectOne) { |
| predictor()->PreconnectUrl( |
| embedded_test_server()->base_url(), GURL(), |
| @@ -1132,17 +1299,14 @@ IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCyclePreresolve) { |
| WaitUntilHostHasBeenRequested(target_url_.host()); |
| } |
| -// 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) { |
| +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")); |
| + |
| int hostnames_requested_before_load = RequestedHostnameCount(); |
| - ui_test_utils::NavigateToURL( |
| - browser(), |
| - GURL(embedded_test_server()->GetURL("/predictor/dns_prefetch.html"))); |
| + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL( |
| + "/predictor/dns_prefetch.html")); |
| WaitUntilHostHasBeenRequested(kChromiumHostname); |
| ASSERT_FALSE(HasHostBeenRequested(kInvalidLongHostname)); |
| ASSERT_EQ(hostnames_requested_before_load + 1, RequestedHostnameCount()); |