summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net/dns_master_unittest.cc
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-26 15:10:43 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-26 15:10:43 +0000
commitfdc58a9963abb0a9f4cac157b2f3f1eac8e4160d (patch)
tree889dd032162f3c876c2bdcaa84a8b3717f134995 /chrome/browser/net/dns_master_unittest.cc
parent9127ca05f3d1cc8308a35429876b18090814ae73 (diff)
downloadchromium_src-fdc58a9963abb0a9f4cac157b2f3f1eac8e4160d.zip
chromium_src-fdc58a9963abb0a9f4cac157b2f3f1eac8e4160d.tar.gz
chromium_src-fdc58a9963abb0a9f4cac157b2f3f1eac8e4160d.tar.bz2
Clean up dns prefetch code, and also port it.
BUG=5687, 6683 Review URL: http://codereview.chromium.org/15076 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8625 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net/dns_master_unittest.cc')
-rw-r--r--chrome/browser/net/dns_master_unittest.cc408
1 files changed, 153 insertions, 255 deletions
diff --git a/chrome/browser/net/dns_master_unittest.cc b/chrome/browser/net/dns_master_unittest.cc
index 650a8ff..70c263c2 100644
--- a/chrome/browser/net/dns_master_unittest.cc
+++ b/chrome/browser/net/dns_master_unittest.cc
@@ -2,104 +2,89 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Multi-threaded tests of DnsMaster and DnsPrefetch slave functionality.
-
#include <time.h>
-#include <ws2tcpip.h>
-#include <Wspiapi.h> // Needed for win2k compatibility
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
+#include <vector>
+#include "base/message_loop.h"
#include "base/platform_thread.h"
#include "base/spin_wait.h"
+#include "base/timer.h"
#include "chrome/browser/net/dns_global.h"
#include "chrome/browser/net/dns_host_info.h"
-#include "chrome/browser/net/dns_slave.h"
+#include "chrome/common/net/dns.h"
+#include "net/base/address_list.h"
+#include "net/base/host_resolver.h"
+#include "net/base/scoped_host_mapper.h"
#include "net/base/winsock_init.h"
#include "testing/gtest/include/gtest/gtest.h"
-
using base::Time;
using base::TimeDelta;
-namespace {
+namespace chrome_browser_net {
-class DnsMasterTest : public testing::Test {
-};
+class WaitForResolutionHelper;
-typedef chrome_browser_net::DnsMaster DnsMaster;
-typedef chrome_browser_net::DnsPrefetcherInit DnsPrefetcherInit;
-typedef chrome_browser_net::DnsHostInfo DnsHostInfo;
-typedef chrome_browser_net::NameList NameList;
+typedef base::RepeatingTimer<WaitForResolutionHelper> HelperTimer;
+class WaitForResolutionHelper {
+ public:
+ WaitForResolutionHelper(DnsMaster* master, const NameList& hosts,
+ HelperTimer* timer)
+ : master_(master),
+ hosts_(hosts),
+ timer_(timer) {
+ }
-//------------------------------------------------------------------------------
-// Provide network function stubs to run tests offline (and avoid the variance
-// of real DNS lookups.
-//------------------------------------------------------------------------------
+ void Run() {
+ for (NameList::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i)
+ if (master_->GetResolutionDuration(*i) == DnsHostInfo::kNullDuration)
+ return; // We don't have resolution for that host.
-static void __stdcall fake_free_addr_info(struct addrinfo* ai) {
- // Kill off the dummy results.
- EXPECT_TRUE(NULL != ai);
- delete ai;
-}
+ // When all hostnames have been resolved, exit the loop.
+ timer_->Stop();
+ MessageLoop::current()->Quit();
+ delete timer_;
+ delete this;
+ }
-static int __stdcall fake_get_addr_info(const char* nodename,
- const char* servname,
- const struct addrinfo* hints,
- struct addrinfo** result) {
- static Lock lock;
- int duration;
- bool was_found;
- std::string hostname(nodename);
- // Dummy up *some* return results to pass along.
- *result = new addrinfo;
- EXPECT_TRUE(NULL != *result);
- {
- AutoLock autolock(lock);
-
- static bool initialized = false;
- typedef std::map<std::string, int> Latency;
- static Latency latency;
- static std::map<std::string, bool> found;
- if (!initialized) {
- initialized = true;
- // List all known hostnames
- latency["www.google.com"] = 50;
- latency["gmail.google.com.com"] = 70;
- latency["mail.google.com"] = 44;
- latency["gmail.com"] = 63;
-
- for (Latency::iterator it = latency.begin(); latency.end() != it; it++) {
- found[it->first] = true;
- }
- } // End static initialization
-
- was_found = found[hostname];
-
- if (latency.end() != latency.find(hostname)) {
- duration = latency[hostname];
- } else {
- duration = 500;
- }
- // Change latency to simulate cache warming (next latency will be short).
- latency[hostname] = 1;
- } // Release lock.
-
- PlatformThread::Sleep(duration);
-
- return was_found ? 0 : WSAHOST_NOT_FOUND;
-}
+ private:
+ DnsMaster* master_;
+ const NameList hosts_;
+ HelperTimer* timer_;
+};
-static void SetupNetworkInfrastructure() {
- bool kUseFakeNetwork = true;
- if (kUseFakeNetwork)
- chrome_browser_net::SetAddrinfoCallbacks(fake_get_addr_info,
- fake_free_addr_info);
-}
+class DnsMasterTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+#if defined(OS_WIN)
+ net::EnsureWinsockInit();
+#endif
+ mapper_.AddRuleWithLatency("www.google.com", "127.0.0.1", 50);
+ mapper_.AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70);
+ mapper_.AddRuleWithLatency("mail.google.com", "127.0.0.1", 44);
+ mapper_.AddRuleWithLatency("gmail.com", "127.0.0.1", 63);
+
+ mapper_.AddRuleWithLatency("veryslow.net", "127.0.0.1", 10000);
+ }
+
+ void WaitForResolution(DnsMaster* master, const NameList& hosts) {
+ HelperTimer* timer = new HelperTimer();
+ timer->Start(TimeDelta::FromMilliseconds(100),
+ new WaitForResolutionHelper(master, hosts, timer),
+ &WaitForResolutionHelper::Run);
+ MessageLoop::current()->Run();
+ }
+
+ private:
+ MessageLoop loop_;
+ net::ScopedHostMapper mapper_;
+};
//------------------------------------------------------------------------------
// Provide a function to create unique (nonexistant) domains at *every* call.
@@ -120,53 +105,63 @@ static std::string GetNonexistantDomain() {
// Use a blocking function to contrast results we get via async services.
//------------------------------------------------------------------------------
TimeDelta BlockingDnsLookup(const std::string& hostname) {
- char* port = "80"; // I may need to get the real port
- struct addrinfo* result = NULL;
Time start = Time::Now();
- // Use the same underlying methods as dns_prefetch_slave does
- chrome_browser_net::get_getaddrinfo()(hostname.c_str(), port,
- NULL, &result);
-
- TimeDelta duration = Time::Now() - start;
-
- if (result) {
- chrome_browser_net::get_freeaddrinfo()(result);
- result = NULL;
- }
+ net::HostResolver resolver;
+ net::AddressList addresses;
+ resolver.Resolve(hostname, 80, &addresses, NULL);
- return duration;
+ return Time::Now() - start;
}
//------------------------------------------------------------------------------
// First test to be sure the OS is caching lookups, which is the whole premise
// of DNS prefetching.
-TEST(DnsMasterTest, OsCachesLookupsTest) {
- SetupNetworkInfrastructure();
- net::EnsureWinsockInit();
-
- for (int i = 0; i < 5; i++) {
+TEST_F(DnsMasterTest, OsCachesLookupsTest) {
+ const Time start = Time::Now();
+ int all_lookups = 0;
+ int lookups_with_improvement = 0;
+ // This test can be really flaky on Linux. It should run in much shorter time,
+ // but sometimes it won't and we don't like bogus failures.
+ while (Time::Now() - start < TimeDelta::FromMinutes(1)) {
std::string badname;
badname = GetNonexistantDomain();
+
TimeDelta duration = BlockingDnsLookup(badname);
- TimeDelta cached_duration = BlockingDnsLookup(badname);
- EXPECT_TRUE(duration > cached_duration);
+
+ // Produce more than one result and remove the largest one
+ // to reduce flakiness.
+ std::vector<TimeDelta> cached_results;
+ for (int j = 0; j < 3; j++)
+ cached_results.push_back(BlockingDnsLookup(badname));
+ std::sort(cached_results.begin(), cached_results.end());
+ cached_results.pop_back();
+
+ TimeDelta cached_sum = TimeDelta::FromSeconds(0);
+ for (std::vector<TimeDelta>::const_iterator j = cached_results.begin();
+ j != cached_results.end(); ++j)
+ cached_sum += *j;
+ TimeDelta cached_duration = cached_sum / cached_results.size();
+
+ all_lookups++;
+ if (cached_duration < duration)
+ lookups_with_improvement++;
+ if (all_lookups >= 10)
+ if (lookups_with_improvement * 100 > all_lookups * 75)
+ // Okay, we see the improvement for more than 75% of all lookups.
+ return;
}
+ FAIL() << "No substantial improvement in lookup time.";
}
-TEST(DnsMasterTest, StartupShutdownTest) {
- DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
-
- // With no threads, we should have no problem doing a shutdown.
- EXPECT_TRUE(testing_master.ShutdownSlaves());
+TEST_F(DnsMasterTest, StartupShutdownTest) {
+ DnsMaster testing_master;
+ // We do shutdown on destruction.
}
-TEST(DnsMasterTest, BenefitLookupTest) {
- SetupNetworkInfrastructure();
- net::EnsureWinsockInit();
- DnsPrefetcherInit dns_init(NULL); // Creates global service .
- DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
+TEST_F(DnsMasterTest, BenefitLookupTest) {
+ DnsMaster testing_master;
std::string goog("www.google.com"),
goog2("gmail.google.com.com"),
@@ -196,23 +191,9 @@ TEST(DnsMasterTest, BenefitLookupTest) {
names.insert(names.end(), goog3);
names.insert(names.end(), goog4);
- // First only cause a minimal set of threads to start up.
- // Currently we actually start 4 threads when we get called with an array
testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
- // Wait for some resoultion for each google.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog2).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog3).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog4).InMilliseconds());
-
- EXPECT_EQ(std::min(names.size(),
- 4u /* chrome_browser_net::DnsMaster::kSlaveCountMin */ ),
- testing_master.running_slave_count());
+ WaitForResolution(&testing_master, names);
EXPECT_TRUE(testing_master.WasFound(goog));
EXPECT_TRUE(testing_master.WasFound(goog2));
@@ -234,68 +215,51 @@ TEST(DnsMasterTest, BenefitLookupTest) {
EXPECT_FALSE(testing_master.AccruePrefetchBenefits(GURL(), &goog2_info));
EXPECT_FALSE(testing_master.AccruePrefetchBenefits(GURL(), &goog3_info));
EXPECT_FALSE(testing_master.AccruePrefetchBenefits(GURL(), &goog4_info));
-
- // Ensure a clean shutdown.
- EXPECT_TRUE(testing_master.ShutdownSlaves());
}
-TEST(DnsMasterTest, DISABLED_SingleSlaveLookupTest) {
- SetupNetworkInfrastructure();
- net::EnsureWinsockInit();
- DnsPrefetcherInit dns_init(NULL); // Creates global service.
- DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
+TEST_F(DnsMasterTest, ShutdownWhenResolutionIsPendingTest) {
+ DnsMaster testing_master;
- std::string goog("www.google.com"),
- goog2("gmail.google.com.com"),
- goog3("mail.google.com"),
- goog4("gmail.com");
- std::string bad1(GetNonexistantDomain()),
- bad2(GetNonexistantDomain());
+ std::string slow("veryslow.net");
+ NameList names;
+ names.insert(names.end(), slow);
- // Warm up local OS cache.
- BlockingDnsLookup(goog);
+ testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ new MessageLoop::QuitTask(), 500);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_FALSE(testing_master.WasFound(slow));
+}
+
+TEST_F(DnsMasterTest, SingleLookupTest) {
+ DnsMaster testing_master;
+
+ std::string goog("www.google.com");
NameList names;
names.insert(names.end(), goog);
- names.insert(names.end(), bad1);
- names.insert(names.end(), bad2);
- // First only cause a single thread to start up
- testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
+ // Try to flood the master with many concurrent requests.
+ for (int i = 0; i < 10; i++)
+ testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
- // Wait for some resoultion for google.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog).InMilliseconds());
+ WaitForResolution(&testing_master, names);
EXPECT_TRUE(testing_master.WasFound(goog));
- EXPECT_FALSE(testing_master.WasFound(bad1));
- EXPECT_FALSE(testing_master.WasFound(bad2));
- // Verify the reason it is not found is that it is still being proceessed.
- // Negative time mean no resolution yet.
- EXPECT_GT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds());
-
- // Spin long enough that we *do* find the resolution of bad2.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(bad2).InMilliseconds());
-
- // Verify both fictitious names are resolved by now.
- // Typical random name takes about 20-30 ms
- EXPECT_LT(0, testing_master.GetResolutionDuration(bad1).InMilliseconds());
- EXPECT_LT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds());
- EXPECT_FALSE(testing_master.WasFound(bad1));
- EXPECT_FALSE(testing_master.WasFound(bad2));
- EXPECT_EQ(1U, testing_master.running_slave_count());
+ MessageLoop::current()->RunAllPending();
- // With just one thread (doing nothing now), ensure a clean shutdown.
- EXPECT_TRUE(testing_master.ShutdownSlaves());
+ EXPECT_GT(testing_master.peak_pending_lookups(), names.size() / 2);
+ EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
+ EXPECT_LE(testing_master.peak_pending_lookups(),
+ DnsMaster::kMaxConcurrentLookups);
}
-TEST(DnsMasterTest, DISABLED_MultiThreadedLookupTest) {
- SetupNetworkInfrastructure();
- net::EnsureWinsockInit();
- DnsMaster testing_master(TimeDelta::FromSeconds(30));
- DnsPrefetcherInit dns_init(NULL);
+TEST_F(DnsMasterTest, ConcurrentLookupTest) {
+ DnsMaster testing_master;
std::string goog("www.google.com"),
goog2("gmail.google.com.com"),
@@ -319,12 +283,11 @@ TEST(DnsMasterTest, DISABLED_MultiThreadedLookupTest) {
BlockingDnsLookup(goog3);
BlockingDnsLookup(goog4);
- // Get all 8 threads running by calling many times before queue is handled.
- for (int i = 0; i < 10; i++) {
+ // Try to flood the master with many concurrent requests.
+ for (int i = 0; i < 10; i++)
testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
- }
- Sleep(10); // Allow time for async DNS to get answers.
+ WaitForResolution(&testing_master, names);
EXPECT_TRUE(testing_master.WasFound(goog));
EXPECT_TRUE(testing_master.WasFound(goog3));
@@ -333,100 +296,35 @@ TEST(DnsMasterTest, DISABLED_MultiThreadedLookupTest) {
EXPECT_FALSE(testing_master.WasFound(bad1));
EXPECT_FALSE(testing_master.WasFound(bad2));
- EXPECT_EQ(8U, testing_master.running_slave_count());
-
- EXPECT_TRUE(testing_master.ShutdownSlaves());
-}
-
-TEST(DnsMasterTest, DISABLED_MultiThreadedSpeedupTest) {
- SetupNetworkInfrastructure();
- net::EnsureWinsockInit();
- DnsMaster testing_master(TimeDelta::FromSeconds(30));
- DnsPrefetcherInit dns_init(NULL);
+ MessageLoop::current()->RunAllPending();
- std::string goog("www.google.com"),
- goog2("gmail.google.com.com"),
- goog3("mail.google.com"),
- goog4("gmail.com");
- std::string bad1(GetNonexistantDomain()),
- bad2(GetNonexistantDomain()),
- bad3(GetNonexistantDomain()),
- bad4(GetNonexistantDomain());
-
- NameList names;
- names.insert(names.end(), goog);
- names.insert(names.end(), bad1);
- names.insert(names.end(), bad2);
- names.insert(names.end(), goog3);
- names.insert(names.end(), goog2);
- names.insert(names.end(), bad3);
- names.insert(names.end(), bad4);
- names.insert(names.end(), goog4);
-
- // First cause a lookup using a single thread.
- testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
-
- // Wait for some resoultion for google.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog).InMilliseconds());
-
- EXPECT_TRUE(testing_master.WasFound(goog));
EXPECT_FALSE(testing_master.WasFound(bad1));
EXPECT_FALSE(testing_master.WasFound(bad2));
- // ...and due to delay in geting resolution of bad names, the single slave
- // thread won't have time to finish the list.
- EXPECT_FALSE(testing_master.WasFound(goog3));
- EXPECT_FALSE(testing_master.WasFound(goog2));
- EXPECT_FALSE(testing_master.WasFound(goog4));
-
- EXPECT_EQ(1U, testing_master.running_slave_count());
- // Get all 8 threads running by calling many times before queue is handled.
- names.clear();
- for (int i = 0; i < 10; i++)
- testing_master.Resolve(GetNonexistantDomain(),
- DnsHostInfo::PAGE_SCAN_MOTIVATED);
-
- // Wait long enough for all the goog's to be resolved.
- // They should all take about the same time, and run in parallel.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog2).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog3).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(goog4).InMilliseconds());
-
- EXPECT_TRUE(testing_master.WasFound(goog3));
- EXPECT_TRUE(testing_master.WasFound(goog2));
- EXPECT_TRUE(testing_master.WasFound(goog4));
-
- EXPECT_FALSE(testing_master.WasFound(bad1));
- EXPECT_FALSE(testing_master.WasFound(bad2)); // Perhaps not even decided.
-
- // Queue durations should be distinct from when 1 slave was working.
- EXPECT_GT(testing_master.GetQueueDuration(goog3).InMilliseconds(),
- testing_master.GetQueueDuration(goog).InMilliseconds());
- EXPECT_GT(testing_master.GetQueueDuration(goog4).InMilliseconds(),
- testing_master.GetQueueDuration(goog).InMilliseconds());
+ EXPECT_GT(testing_master.peak_pending_lookups(), names.size() / 2);
+ EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
+ EXPECT_LE(testing_master.peak_pending_lookups(),
+ DnsMaster::kMaxConcurrentLookups);
+}
- // Give bad names a chance to be determined as unresolved.
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(bad1).InMilliseconds());
- SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
- testing_master.GetResolutionDuration(bad2).InMilliseconds());
+TEST_F(DnsMasterTest, MassiveConcurrentLookupTest) {
+ DnsMaster testing_master;
+ NameList names;
+ for (int i = 0; i < 100; i++)
+ names.push_back(GetNonexistantDomain());
- // Well known names should resolve faster than bad names.
- EXPECT_GE(testing_master.GetResolutionDuration(bad1).InMilliseconds(),
- testing_master.GetResolutionDuration(goog).InMilliseconds());
+ // Try to flood the master with many concurrent requests.
+ for (int i = 0; i < 10; i++)
+ testing_master.ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
- EXPECT_GE(testing_master.GetResolutionDuration(bad2).InMilliseconds(),
- testing_master.GetResolutionDuration(goog4).InMilliseconds());
+ WaitForResolution(&testing_master, names);
- EXPECT_EQ(8U, testing_master.running_slave_count());
+ MessageLoop::current()->RunAllPending();
- EXPECT_TRUE(testing_master.ShutdownSlaves());
+ EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
+ EXPECT_LE(testing_master.peak_pending_lookups(),
+ DnsMaster::kMaxConcurrentLookups);
}
-} // namespace
-
+} // namespace chrome_browser_net