• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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 <algorithm>
6 #include <string>
7 #include <vector>
8 
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/perftimer.h"
12 #include "base/shared_memory.h"
13 #include "base/string_util.h"
14 #include "base/test/test_file_util.h"
15 #include "chrome/browser/visitedlink/visitedlink_master.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 using base::TimeDelta;
19 
20 namespace {
21 
22 // how we generate URLs, note that the two strings should be the same length
23 const int add_count = 10000;
24 const int load_test_add_count = 250000;
25 const char added_prefix[] = "http://www.google.com/stuff/something/foo?session=85025602345625&id=1345142319023&seq=";
26 const char unadded_prefix[] = "http://www.google.org/stuff/something/foo?session=39586739476365&id=2347624314402&seq=";
27 
28 // Returns a URL with the given prefix and index
TestURL(const char * prefix,int i)29 GURL TestURL(const char* prefix, int i) {
30   return GURL(StringPrintf("%s%d", prefix, i));
31 }
32 
33 // We have no slaves, so all methods on this listener are a no-ops.
34 class DummyVisitedLinkEventListener : public VisitedLinkMaster::Listener {
35  public:
DummyVisitedLinkEventListener()36   DummyVisitedLinkEventListener() {}
NewTable(base::SharedMemory * table)37   virtual void NewTable(base::SharedMemory* table) {}
Add(VisitedLinkCommon::Fingerprint)38   virtual void Add(VisitedLinkCommon::Fingerprint) {}
Reset()39   virtual void Reset() {}
40 
GetInstance()41   static DummyVisitedLinkEventListener* GetInstance() {
42     static DummyVisitedLinkEventListener instance;
43     return &instance;
44   }
45 };
46 
47 
48 // this checks IsVisited for the URLs starting with the given prefix and
49 // within the given range
CheckVisited(VisitedLinkMaster & master,const char * prefix,int begin,int end)50 void CheckVisited(VisitedLinkMaster& master, const char* prefix,
51                   int begin, int end) {
52   for (int i = begin; i < end; i++)
53     master.IsVisited(TestURL(prefix, i));
54 }
55 
56 // Fills that master's table with URLs starting with the given prefix and
57 // within the given range
FillTable(VisitedLinkMaster & master,const char * prefix,int begin,int end)58 void FillTable(VisitedLinkMaster& master, const char* prefix,
59                int begin, int end) {
60   for (int i = begin; i < end; i++)
61     master.AddURL(TestURL(prefix, i));
62 }
63 
64 class VisitedLink : public testing::Test {
65  protected:
66   FilePath db_path_;
SetUp()67   virtual void SetUp() {
68     ASSERT_TRUE(file_util::CreateTemporaryFile(&db_path_));
69   }
TearDown()70   virtual void TearDown() {
71     file_util::Delete(db_path_, false);
72   }
73 };
74 
75 } // namespace
76 
77 // This test tests adding many things to a database, and how long it takes
78 // to query the database with different numbers of things in it. The time
79 // is the total time to do all the operations, and as such, it is only
80 // useful for a regression test. If there is a regression, it might be
81 // useful to make another set of tests to test these things in isolation.
TEST_F(VisitedLink,TestAddAndQuery)82 TEST_F(VisitedLink, TestAddAndQuery) {
83   // init
84   VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(),
85                            NULL, true, db_path_, 0);
86   ASSERT_TRUE(master.Init());
87 
88   PerfTimeLogger timer("Visited_link_add_and_query");
89 
90   // first check without anything in the table
91   CheckVisited(master, added_prefix, 0, add_count);
92 
93   // now fill half the table
94   const int half_size = add_count / 2;
95   FillTable(master, added_prefix, 0, half_size);
96 
97   // check the table again, half of these URLs will be visited, the other half
98   // will not
99   CheckVisited(master, added_prefix, 0, add_count);
100 
101   // fill the rest of the table
102   FillTable(master, added_prefix, half_size, add_count);
103 
104   // check URLs, doing half visited, half unvisited
105   CheckVisited(master, added_prefix, 0, add_count);
106   CheckVisited(master, unadded_prefix, 0, add_count);
107 }
108 
109 // Tests how long it takes to write and read a large database to and from disk.
TEST_F(VisitedLink,TestLoad)110 TEST_F(VisitedLink, TestLoad) {
111   // create a big DB
112   {
113     PerfTimeLogger table_initialization_timer("Table_initialization");
114 
115     VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(),
116                              NULL, true, db_path_, 0);
117 
118     // time init with empty table
119     PerfTimeLogger initTimer("Empty_visited_link_init");
120     bool success = master.Init();
121     initTimer.Done();
122     ASSERT_TRUE(success);
123 
124     // add a bunch of stuff
125     // TODO(maruel): This is very inefficient because the file gets rewritten
126     // many time and this is the actual bottleneck of this test. The file should
127     // only get written that the end of the FillTable call, not 4169(!) times.
128     FillTable(master, added_prefix, 0, load_test_add_count);
129 
130     // time writing the file out out
131     PerfTimeLogger flushTimer("Visited_link_database_flush");
132     master.RewriteFile();
133     // TODO(maruel): Without calling FlushFileBuffers(master.file_); you don't
134     // know really how much time it took to write the file.
135     flushTimer.Done();
136 
137     table_initialization_timer.Done();
138   }
139 
140   // test loading the DB back, we do this several times since the flushing is
141   // not very reliable.
142   const int load_count = 5;
143   std::vector<double> cold_load_times;
144   std::vector<double> hot_load_times;
145   for (int i = 0; i < load_count; i++) {
146     // make sure the file has to be re-loaded
147     file_util::EvictFileFromSystemCache(db_path_);
148 
149     // cold load (no OS cache, hopefully)
150     {
151       PerfTimer cold_timer;
152 
153       VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(),
154                                NULL,
155                                true,
156                                db_path_,
157                                0);
158       bool success = master.Init();
159       TimeDelta elapsed = cold_timer.Elapsed();
160       ASSERT_TRUE(success);
161 
162       cold_load_times.push_back(elapsed.InMillisecondsF());
163     }
164 
165     // hot load (with OS caching the file in memory)
166     {
167       PerfTimer hot_timer;
168 
169       VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(),
170                                NULL,
171                                true,
172                                db_path_,
173                                0);
174       bool success = master.Init();
175       TimeDelta elapsed = hot_timer.Elapsed();
176       ASSERT_TRUE(success);
177 
178       hot_load_times.push_back(elapsed.InMillisecondsF());
179     }
180   }
181 
182   // We discard the max and return the average time.
183   cold_load_times.erase(std::max_element(cold_load_times.begin(),
184                                          cold_load_times.end()));
185   hot_load_times.erase(std::max_element(hot_load_times.begin(),
186                                         hot_load_times.end()));
187 
188   double cold_sum = 0, hot_sum = 0;
189   for (int i = 0; i < static_cast<int>(cold_load_times.size()); i++) {
190     cold_sum += cold_load_times[i];
191     hot_sum += hot_load_times[i];
192   }
193   LogPerfResult("Visited_link_cold_load_time",
194                 cold_sum / cold_load_times.size(), "ms");
195   LogPerfResult("Visited_link_hot_load_time",
196                 hot_sum / hot_load_times.size(), "ms");
197 }
198