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/files/file_path.h"
10#include "base/files/file_util.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
21using base::TimeDelta;
22
23namespace visitedlink {
24
25namespace {
26
27// how we generate URLs, note that the two strings should be the same length
28const int add_count = 10000;
29const int load_test_add_count = 250000;
30const char added_prefix[] = "http://www.google.com/stuff/something/foo?session=85025602345625&id=1345142319023&seq=";
31const 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
34GURL 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.
39class DummyVisitedLinkEventListener : public VisitedLinkMaster::Listener {
40 public:
41  DummyVisitedLinkEventListener() {}
42  virtual void NewTable(base::SharedMemory* table) OVERRIDE {}
43  virtual void Add(VisitedLinkCommon::Fingerprint) OVERRIDE {}
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
50void 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
58void 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
64class VisitedLink : public testing::Test {
65 protected:
66  base::FilePath db_path_;
67  virtual void SetUp() {
68    ASSERT_TRUE(base::CreateTemporaryFile(&db_path_));
69  }
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.
82TEST_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.
110TEST_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    base::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