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_util.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/shared_memory.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/test/perf_log.h"
14 #include "base/test/perf_time_logger.h"
15 #include "base/test/test_file_util.h"
16 #include "base/timer/elapsed_timer.h"
17 #include "components/visitedlink/browser/visitedlink_master.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "url/gurl.h"
20
21 using base::TimeDelta;
22
23 namespace visitedlink {
24
25 namespace {
26
27 // how we generate URLs, note that the two strings should be the same length
28 const int add_count = 10000;
29 const int load_test_add_count = 250000;
30 const char added_prefix[] = "http://www.google.com/stuff/something/foo?session=85025602345625&id=1345142319023&seq=";
31 const char unadded_prefix[] = "http://www.google.org/stuff/something/foo?session=39586739476365&id=2347624314402&seq=";
32
33 // Returns a URL with the given prefix and index
TestURL(const char * prefix,int i)34 GURL TestURL(const char* prefix, int i) {
35 return GURL(base::StringPrintf("%s%d", prefix, i));
36 }
37
38 // We have no slaves, so all methods on this listener are a no-ops.
39 class DummyVisitedLinkEventListener : public VisitedLinkMaster::Listener {
40 public:
DummyVisitedLinkEventListener()41 DummyVisitedLinkEventListener() {}
NewTable(base::SharedMemory * table)42 virtual void NewTable(base::SharedMemory* table) OVERRIDE {}
Add(VisitedLinkCommon::Fingerprint)43 virtual void Add(VisitedLinkCommon::Fingerprint) OVERRIDE {}
Reset()44 virtual void Reset() OVERRIDE {}
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 base::FilePath db_path_;
SetUp()67 virtual void SetUp() {
68 ASSERT_TRUE(base::CreateTemporaryFile(&db_path_));
69 }
TearDown()70 virtual void TearDown() {
71 base::DeleteFile(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(new DummyVisitedLinkEventListener(),
85 NULL, true, true, db_path_, 0);
86 ASSERT_TRUE(master.Init());
87
88 base::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 base::PerfTimeLogger table_initialization_timer("Table_initialization");
114
115 VisitedLinkMaster master(new DummyVisitedLinkEventListener(),
116 NULL, true, true, db_path_, 0);
117
118 // time init with empty table
119 base::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 base::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 base::ElapsedTimer cold_timer;
152
153 VisitedLinkMaster master(new DummyVisitedLinkEventListener(),
154 NULL,
155 true,
156 true,
157 db_path_,
158 0);
159 bool success = master.Init();
160 TimeDelta elapsed = cold_timer.Elapsed();
161 ASSERT_TRUE(success);
162
163 cold_load_times.push_back(elapsed.InMillisecondsF());
164 }
165
166 // hot load (with OS caching the file in memory)
167 {
168 base::ElapsedTimer hot_timer;
169
170 VisitedLinkMaster master(new DummyVisitedLinkEventListener(),
171 NULL,
172 true,
173 true,
174 db_path_,
175 0);
176 bool success = master.Init();
177 TimeDelta elapsed = hot_timer.Elapsed();
178 ASSERT_TRUE(success);
179
180 hot_load_times.push_back(elapsed.InMillisecondsF());
181 }
182 }
183
184 // We discard the max and return the average time.
185 cold_load_times.erase(std::max_element(cold_load_times.begin(),
186 cold_load_times.end()));
187 hot_load_times.erase(std::max_element(hot_load_times.begin(),
188 hot_load_times.end()));
189
190 double cold_sum = 0, hot_sum = 0;
191 for (int i = 0; i < static_cast<int>(cold_load_times.size()); i++) {
192 cold_sum += cold_load_times[i];
193 hot_sum += hot_load_times[i];
194 }
195 base::LogPerfResult(
196 "Visited_link_cold_load_time", cold_sum / cold_load_times.size(), "ms");
197 base::LogPerfResult(
198 "Visited_link_hot_load_time", hot_sum / hot_load_times.size(), "ms");
199 }
200
201 } // namespace visitedlink
202