blob: 2f352369f47a28beff471af37d4cc7f18cdde6b5 [file] [log] [blame]
// 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 <utility>
#include "base/base_paths.h"
#include "base/compiler_specific.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/test/perf_time_logger.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_resolver_factory.h"
#include "net/proxy/proxy_resolver_v8.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include "net/proxy/proxy_resolver_winhttp.h"
#elif defined(OS_MACOSX)
#include "net/proxy/proxy_resolver_mac.h"
#endif
namespace net {
namespace {
// This class holds the URL to use for resolving, and the expected result.
// We track the expected result in order to make sure the performance
// test is actually resolving URLs properly, otherwise the perf numbers
// are meaningless :-)
struct PacQuery {
const char* query_url;
const char* expected_result;
};
// Entry listing which PAC scripts to load, and which URLs to try resolving.
// |queries| should be terminated by {NULL, NULL}. A sentinel is used
// rather than a length, to simplify using initializer lists.
struct PacPerfTest {
const char* pac_name;
PacQuery queries[100];
// Returns the actual number of entries in |queries| (assumes NULL sentinel).
int NumQueries() const;
};
// List of performance tests.
static PacPerfTest kPerfTests[] = {
// This test uses an ad-blocker PAC script. This script is very heavily
// regular expression oriented, and has no dependencies on the current
// IP address, or DNS resolving of hosts.
{ "no-ads.pac",
{ // queries:
{"http://www.google.com", "DIRECT"},
{"http://www.imdb.com/photos/cmsicons/x", "PROXY 0.0.0.0:3421"},
{"http://www.imdb.com/x", "DIRECT"},
{"http://www.staples.com/", "DIRECT"},
{"http://www.staples.com/pixeltracker/x", "PROXY 0.0.0.0:3421"},
{"http://www.staples.com/pixel/x", "DIRECT"},
{"http://www.foobar.com", "DIRECT"},
{"http://www.foobarbaz.com/x/y/z", "DIRECT"},
{"http://www.testurl1.com/index.html", "DIRECT"},
{"http://www.testurl2.com", "DIRECT"},
{"https://www.sample/pirate/arrrrrr", "DIRECT"},
{NULL, NULL}
},
},
};
int PacPerfTest::NumQueries() const {
for (size_t i = 0; i < arraysize(queries); ++i) {
if (queries[i].query_url == NULL)
return i;
}
NOTREACHED(); // Bad definition.
return 0;
}
// The number of URLs to resolve when testing a PAC script.
const int kNumIterations = 500;
// Helper class to run through all the performance tests using the specified
// proxy resolver implementation.
class PacPerfSuiteRunner {
public:
// |resolver_name| is the label used when logging the results.
PacPerfSuiteRunner(ProxyResolverFactory* factory,
const std::string& resolver_name)
: factory_(factory), resolver_name_(resolver_name) {
test_server_.ServeFilesFromSourceDirectory(
"net/data/proxy_resolver_perftest");
}
void RunAllTests() {
ASSERT_TRUE(test_server_.Start());
for (size_t i = 0; i < arraysize(kPerfTests); ++i) {
const PacPerfTest& test_data = kPerfTests[i];
RunTest(test_data.pac_name,
test_data.queries,
test_data.NumQueries());
}
}
private:
void RunTest(const std::string& script_name,
const PacQuery* queries,
int queries_len) {
std::unique_ptr<ProxyResolver> resolver;
if (!factory_->expects_pac_bytes()) {
GURL pac_url = test_server_.GetURL(std::string("/") + script_name);
int rv = factory_->CreateProxyResolver(
ProxyResolverScriptData::FromURL(pac_url), &resolver,
CompletionCallback(), nullptr);
EXPECT_EQ(OK, rv);
} else {
resolver = LoadPacScriptAndCreateResolver(script_name);
}
ASSERT_TRUE(resolver);
// Do a query to warm things up. In the case of internal-fetch proxy
// resolvers, the first resolve will be slow since it has to download
// the PAC script.
{
ProxyInfo proxy_info;
int result =
resolver->GetProxyForURL(GURL("http://www.warmup.com"), &proxy_info,
CompletionCallback(), NULL, BoundNetLog());
ASSERT_EQ(OK, result);
}
// Start the perf timer.
std::string perf_test_name = resolver_name_ + "_" + script_name;
base::PerfTimeLogger timer(perf_test_name.c_str());
for (int i = 0; i < kNumIterations; ++i) {
// Round-robin between URLs to resolve.
const PacQuery& query = queries[i % queries_len];
// Resolve.
ProxyInfo proxy_info;
int result =
resolver->GetProxyForURL(GURL(query.query_url), &proxy_info,
CompletionCallback(), NULL, BoundNetLog());
// Check that the result was correct. Note that ToPacString() and
// ASSERT_EQ() are fast, so they won't skew the results.
ASSERT_EQ(OK, result);
ASSERT_EQ(query.expected_result, proxy_info.ToPacString());
}
// Print how long the test ran for.
timer.Done();
}
// Read the PAC script from disk and initialize the proxy resolver with it.
std::unique_ptr<ProxyResolver> LoadPacScriptAndCreateResolver(
const std::string& script_name) {
base::FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.AppendASCII("net");
path = path.AppendASCII("data");
path = path.AppendASCII("proxy_resolver_perftest");
path = path.AppendASCII(script_name);
// Try to read the file from disk.
std::string file_contents;
bool ok = base::ReadFileToString(path, &file_contents);
// If we can't load the file from disk, something is misconfigured.
LOG_IF(ERROR, !ok) << "Failed to read file: " << path.value();
if (!ok)
return nullptr;
// Load the PAC script into the ProxyResolver.
std::unique_ptr<ProxyResolver> resolver;
int rv = factory_->CreateProxyResolver(
ProxyResolverScriptData::FromUTF8(file_contents), &resolver,
CompletionCallback(), nullptr);
EXPECT_EQ(OK, rv);
return resolver;
}
ProxyResolverFactory* factory_;
std::string resolver_name_;
EmbeddedTestServer test_server_;
};
#if defined(OS_WIN)
TEST(ProxyResolverPerfTest, ProxyResolverWinHttp) {
ProxyResolverFactoryWinHttp factory;
PacPerfSuiteRunner runner(&factory, "ProxyResolverWinHttp");
runner.RunAllTests();
}
#elif defined(OS_MACOSX)
TEST(ProxyResolverPerfTest, ProxyResolverMac) {
ProxyResolverFactoryMac factory;
PacPerfSuiteRunner runner(&factory, "ProxyResolverMac");
runner.RunAllTests();
}
#endif
class MockJSBindings : public ProxyResolverV8::JSBindings {
public:
MockJSBindings() {}
void Alert(const base::string16& message) override { CHECK(false); }
bool ResolveDns(const std::string& host,
ResolveDnsOperation op,
std::string* output,
bool* terminate) override {
CHECK(false);
return false;
}
void OnError(int line_number, const base::string16& message) override {
CHECK(false);
}
};
class ProxyResolverV8Wrapper : public ProxyResolver {
public:
ProxyResolverV8Wrapper(std::unique_ptr<ProxyResolverV8> resolver,
std::unique_ptr<MockJSBindings> bindings)
: resolver_(std::move(resolver)), bindings_(std::move(bindings)) {}
int GetProxyForURL(const GURL& url,
ProxyInfo* results,
const CompletionCallback& /*callback*/,
RequestHandle* /*request*/,
const BoundNetLog& net_log) override {
return resolver_->GetProxyForURL(url, results, bindings_.get());
}
void CancelRequest(RequestHandle request) override { NOTREACHED(); }
LoadState GetLoadState(RequestHandle request) const override {
NOTREACHED();
return LOAD_STATE_IDLE;
}
private:
std::unique_ptr<ProxyResolverV8> resolver_;
std::unique_ptr<MockJSBindings> bindings_;
DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Wrapper);
};
class ProxyResolverV8Factory : public ProxyResolverFactory {
public:
ProxyResolverV8Factory() : ProxyResolverFactory(true) {}
int CreateProxyResolver(
const scoped_refptr<ProxyResolverScriptData>& pac_script,
std::unique_ptr<ProxyResolver>* resolver,
const net::CompletionCallback& callback,
std::unique_ptr<Request>* request) override {
std::unique_ptr<ProxyResolverV8> v8_resolver;
std::unique_ptr<MockJSBindings> js_bindings_(new MockJSBindings);
int result =
ProxyResolverV8::Create(pac_script, js_bindings_.get(), &v8_resolver);
if (result == OK) {
resolver->reset(new ProxyResolverV8Wrapper(std::move(v8_resolver),
std::move(js_bindings_)));
}
return result;
}
private:
DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Factory);
};
TEST(ProxyResolverPerfTest, ProxyResolverV8) {
base::MessageLoop message_loop;
ProxyResolverV8Factory factory;
PacPerfSuiteRunner runner(&factory, "ProxyResolverV8");
runner.RunAllTests();
}
} // namespace
} // namespace net