1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/base_paths.h" 6 #include "base/compiler_specific.h" 7 #include "base/file_util.h" 8 #include "base/path_service.h" 9 #include "base/strings/string_util.h" 10 #include "base/test/perf_time_logger.h" 11 #include "net/base/net_errors.h" 12 #include "net/dns/mock_host_resolver.h" 13 #include "net/proxy/proxy_info.h" 14 #include "net/proxy/proxy_resolver_v8.h" 15 #include "net/test/spawned_test_server/spawned_test_server.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 #if defined(OS_WIN) 19 #include "net/proxy/proxy_resolver_winhttp.h" 20 #elif defined(OS_MACOSX) 21 #include "net/proxy/proxy_resolver_mac.h" 22 #endif 23 24 // This class holds the URL to use for resolving, and the expected result. 25 // We track the expected result in order to make sure the performance 26 // test is actually resolving URLs properly, otherwise the perf numbers 27 // are meaningless :-) 28 struct PacQuery { 29 const char* query_url; 30 const char* expected_result; 31 }; 32 33 // Entry listing which PAC scripts to load, and which URLs to try resolving. 34 // |queries| should be terminated by {NULL, NULL}. A sentinel is used 35 // rather than a length, to simplify using initializer lists. 36 struct PacPerfTest { 37 const char* pac_name; 38 PacQuery queries[100]; 39 40 // Returns the actual number of entries in |queries| (assumes NULL sentinel). 41 int NumQueries() const; 42 }; 43 44 // List of performance tests. 45 static PacPerfTest kPerfTests[] = { 46 // This test uses an ad-blocker PAC script. This script is very heavily 47 // regular expression oriented, and has no dependencies on the current 48 // IP address, or DNS resolving of hosts. 49 { "no-ads.pac", 50 { // queries: 51 {"http://www.google.com", "DIRECT"}, 52 {"http://www.imdb.com/photos/cmsicons/x", "PROXY 0.0.0.0:3421"}, 53 {"http://www.imdb.com/x", "DIRECT"}, 54 {"http://www.staples.com/", "DIRECT"}, 55 {"http://www.staples.com/pixeltracker/x", "PROXY 0.0.0.0:3421"}, 56 {"http://www.staples.com/pixel/x", "DIRECT"}, 57 {"http://www.foobar.com", "DIRECT"}, 58 {"http://www.foobarbaz.com/x/y/z", "DIRECT"}, 59 {"http://www.testurl1.com/index.html", "DIRECT"}, 60 {"http://www.testurl2.com", "DIRECT"}, 61 {"https://www.sample/pirate/arrrrrr", "DIRECT"}, 62 {NULL, NULL} 63 }, 64 }, 65 }; 66 67 int PacPerfTest::NumQueries() const { 68 for (size_t i = 0; i < arraysize(queries); ++i) { 69 if (queries[i].query_url == NULL) 70 return i; 71 } 72 NOTREACHED(); // Bad definition. 73 return 0; 74 } 75 76 // The number of URLs to resolve when testing a PAC script. 77 const int kNumIterations = 500; 78 79 // Helper class to run through all the performance tests using the specified 80 // proxy resolver implementation. 81 class PacPerfSuiteRunner { 82 public: 83 // |resolver_name| is the label used when logging the results. 84 PacPerfSuiteRunner(net::ProxyResolver* resolver, 85 const std::string& resolver_name) 86 : resolver_(resolver), 87 resolver_name_(resolver_name), 88 test_server_( 89 net::SpawnedTestServer::TYPE_HTTP, 90 net::SpawnedTestServer::kLocalhost, 91 base::FilePath( 92 FILE_PATH_LITERAL("net/data/proxy_resolver_perftest"))) { 93 } 94 95 void RunAllTests() { 96 ASSERT_TRUE(test_server_.Start()); 97 for (size_t i = 0; i < arraysize(kPerfTests); ++i) { 98 const PacPerfTest& test_data = kPerfTests[i]; 99 RunTest(test_data.pac_name, 100 test_data.queries, 101 test_data.NumQueries()); 102 } 103 } 104 105 private: 106 void RunTest(const std::string& script_name, 107 const PacQuery* queries, 108 int queries_len) { 109 if (!resolver_->expects_pac_bytes()) { 110 GURL pac_url = 111 test_server_.GetURL(std::string("files/") + script_name); 112 int rv = resolver_->SetPacScript( 113 net::ProxyResolverScriptData::FromURL(pac_url), 114 net::CompletionCallback()); 115 EXPECT_EQ(net::OK, rv); 116 } else { 117 LoadPacScriptIntoResolver(script_name); 118 } 119 120 // Do a query to warm things up. In the case of internal-fetch proxy 121 // resolvers, the first resolve will be slow since it has to download 122 // the PAC script. 123 { 124 net::ProxyInfo proxy_info; 125 int result = resolver_->GetProxyForURL( 126 GURL("http://www.warmup.com"), &proxy_info, net::CompletionCallback(), 127 NULL, net::BoundNetLog()); 128 ASSERT_EQ(net::OK, result); 129 } 130 131 // Start the perf timer. 132 std::string perf_test_name = resolver_name_ + "_" + script_name; 133 base::PerfTimeLogger timer(perf_test_name.c_str()); 134 135 for (int i = 0; i < kNumIterations; ++i) { 136 // Round-robin between URLs to resolve. 137 const PacQuery& query = queries[i % queries_len]; 138 139 // Resolve. 140 net::ProxyInfo proxy_info; 141 int result = resolver_->GetProxyForURL( 142 GURL(query.query_url), &proxy_info, net::CompletionCallback(), NULL, 143 net::BoundNetLog()); 144 145 // Check that the result was correct. Note that ToPacString() and 146 // ASSERT_EQ() are fast, so they won't skew the results. 147 ASSERT_EQ(net::OK, result); 148 ASSERT_EQ(query.expected_result, proxy_info.ToPacString()); 149 } 150 151 // Print how long the test ran for. 152 timer.Done(); 153 } 154 155 // Read the PAC script from disk and initialize the proxy resolver with it. 156 void LoadPacScriptIntoResolver(const std::string& script_name) { 157 base::FilePath path; 158 PathService::Get(base::DIR_SOURCE_ROOT, &path); 159 path = path.AppendASCII("net"); 160 path = path.AppendASCII("data"); 161 path = path.AppendASCII("proxy_resolver_perftest"); 162 path = path.AppendASCII(script_name); 163 164 // Try to read the file from disk. 165 std::string file_contents; 166 bool ok = base::ReadFileToString(path, &file_contents); 167 168 // If we can't load the file from disk, something is misconfigured. 169 LOG_IF(ERROR, !ok) << "Failed to read file: " << path.value(); 170 ASSERT_TRUE(ok); 171 172 // Load the PAC script into the ProxyResolver. 173 int rv = resolver_->SetPacScript( 174 net::ProxyResolverScriptData::FromUTF8(file_contents), 175 net::CompletionCallback()); 176 EXPECT_EQ(net::OK, rv); 177 } 178 179 net::ProxyResolver* resolver_; 180 std::string resolver_name_; 181 net::SpawnedTestServer test_server_; 182 }; 183 184 #if defined(OS_WIN) 185 TEST(ProxyResolverPerfTest, ProxyResolverWinHttp) { 186 net::ProxyResolverWinHttp resolver; 187 PacPerfSuiteRunner runner(&resolver, "ProxyResolverWinHttp"); 188 runner.RunAllTests(); 189 } 190 #elif defined(OS_MACOSX) 191 TEST(ProxyResolverPerfTest, ProxyResolverMac) { 192 net::ProxyResolverMac resolver; 193 PacPerfSuiteRunner runner(&resolver, "ProxyResolverMac"); 194 runner.RunAllTests(); 195 } 196 #endif 197 198 class MockJSBindings : public net::ProxyResolverV8::JSBindings { 199 public: 200 MockJSBindings() {} 201 202 virtual void Alert(const base::string16& message) OVERRIDE { 203 CHECK(false); 204 } 205 206 virtual bool ResolveDns(const std::string& host, 207 ResolveDnsOperation op, 208 std::string* output, 209 bool* terminate) OVERRIDE { 210 CHECK(false); 211 return false; 212 } 213 214 virtual void OnError(int line_number, 215 const base::string16& message) OVERRIDE { 216 CHECK(false); 217 } 218 }; 219 220 TEST(ProxyResolverPerfTest, ProxyResolverV8) { 221 // This has to be done on the main thread. 222 net::ProxyResolverV8::RememberDefaultIsolate(); 223 224 MockJSBindings js_bindings; 225 net::ProxyResolverV8 resolver; 226 resolver.set_js_bindings(&js_bindings); 227 PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8"); 228 runner.RunAllTests(); 229 } 230