// 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.
#include "base/json/json_string_value_serializer.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/net/predictor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.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 "net/base/net_errors.h"
#include "net/dns/host_resolver_proc.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
using content::BrowserThread;
using testing::HasSubstr;
namespace {
// 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) {
}
virtual 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;
}
bool HasHostBeenRequested(const std::string& hostname) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return std::find(requested_hostnames_.begin(),
requested_hostnames_.end(),
hostname) != requested_hostnames_.end();
}
void WaitUntilHostHasBeenRequested(const std::string& hostname) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!is_waiting_for_hostname_);
if (HasHostBeenRequested(hostname))
return;
waiting_for_hostname_ = hostname;
is_waiting_for_hostname_ = true;
content::RunMessageLoop();
}
private:
virtual ~HostResolutionRequestRecorder() {}
void AddToHistory(const std::string& hostname) {
DCHECK(BrowserThread::CurrentlyOn(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()->Quit();
}
}
// 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);
};
} // namespace
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) {
}
protected:
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
scoped_host_resolver_proc_.reset(new net::ScopedDefaultHostResolverProc(
host_resolution_request_recorder_.get()));
InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
InProcessBrowserTest::TearDownInProcessBrowserTestFixture();
scoped_host_resolver_proc_.reset();
}
void LearnAboutInitialNavigation(const GURL& url) {
Predictor* predictor = browser()->profile()->GetNetworkPredictor();
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&Predictor::LearnAboutInitialNavigation,
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));
content::RunAllPendingInMessageLoop(BrowserThread::IO);
}
void PrepareFrameSubresources(const GURL& url) {
Predictor* predictor = browser()->profile()->GetNetworkPredictor();
predictor->PredictFrameSubresources(url, GURL());
}
void GetListFromPrefsAsString(const char* list_path,
std::string* value_as_string) const {
PrefService* prefs = browser()->profile()->GetPrefs();
const base::ListValue* list_value = prefs->GetList(list_path);
JSONStringValueSerializer serializer(value_as_string);
serializer.Serialize(*list_value);
}
void WaitUntilHostHasBeenRequested(const std::string& hostname) {
host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname);
}
const GURL startup_url_;
const GURL referring_url_;
const GURL target_url_;
private:
scoped_refptr<HostResolutionRequestRecorder>
host_resolution_request_recorder_;
scoped_ptr<net::ScopedDefaultHostResolverProc> scoped_host_resolver_proc_;
};
IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) {
// Prepare state that will be serialized on this shut-down and read on next
// start-up.
LearnAboutInitialNavigation(startup_url_);
LearnFromNavigation(referring_url_, target_url_);
}
IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) {
// Make sure that the Preferences file is actually wiped of all DNS prefetch
// related data after start-up.
std::string cleared_startup_list;
std::string cleared_referral_list;
GetListFromPrefsAsString(prefs::kDnsPrefetchingStartupList,
&cleared_startup_list);
GetListFromPrefsAsString(prefs::kDnsPrefetchingHostReferralList,
&cleared_referral_list);
EXPECT_THAT(cleared_startup_list, Not(HasSubstr(startup_url_.host())));
EXPECT_THAT(cleared_referral_list, Not(HasSubstr(referring_url_.host())));
EXPECT_THAT(cleared_referral_list, Not(HasSubstr(target_url_.host())));
// 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());
}
} // namespace chrome_browser_net