| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/perftimer.h" |
| #include "base/shared_memory.h" |
| #include "base/string_util.h" |
| #include "base/test/test_file_util.h" |
| #include "chrome/browser/visitedlink/visitedlink_master.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::TimeDelta; |
| |
| namespace { |
| |
| // how we generate URLs, note that the two strings should be the same length |
| const int add_count = 10000; |
| const int load_test_add_count = 250000; |
| const char added_prefix[] = "http://www.google.com/stuff/something/foo?session=85025602345625&id=1345142319023&seq="; |
| const char unadded_prefix[] = "http://www.google.org/stuff/something/foo?session=39586739476365&id=2347624314402&seq="; |
| |
| // Returns a URL with the given prefix and index |
| GURL TestURL(const char* prefix, int i) { |
| return GURL(StringPrintf("%s%d", prefix, i)); |
| } |
| |
| // We have no slaves, so all methods on this listener are a no-ops. |
| class DummyVisitedLinkEventListener : public VisitedLinkMaster::Listener { |
| public: |
| DummyVisitedLinkEventListener() {} |
| virtual void NewTable(base::SharedMemory* table) {} |
| virtual void Add(VisitedLinkCommon::Fingerprint) {} |
| virtual void Reset() {} |
| |
| static DummyVisitedLinkEventListener* GetInstance() { |
| static DummyVisitedLinkEventListener instance; |
| return &instance; |
| } |
| }; |
| |
| |
| // this checks IsVisited for the URLs starting with the given prefix and |
| // within the given range |
| void CheckVisited(VisitedLinkMaster& master, const char* prefix, |
| int begin, int end) { |
| for (int i = begin; i < end; i++) |
| master.IsVisited(TestURL(prefix, i)); |
| } |
| |
| // Fills that master's table with URLs starting with the given prefix and |
| // within the given range |
| void FillTable(VisitedLinkMaster& master, const char* prefix, |
| int begin, int end) { |
| for (int i = begin; i < end; i++) |
| master.AddURL(TestURL(prefix, i)); |
| } |
| |
| class VisitedLink : public testing::Test { |
| protected: |
| FilePath db_path_; |
| virtual void SetUp() { |
| ASSERT_TRUE(file_util::CreateTemporaryFile(&db_path_)); |
| } |
| virtual void TearDown() { |
| file_util::Delete(db_path_, false); |
| } |
| }; |
| |
| } // namespace |
| |
| // This test tests adding many things to a database, and how long it takes |
| // to query the database with different numbers of things in it. The time |
| // is the total time to do all the operations, and as such, it is only |
| // useful for a regression test. If there is a regression, it might be |
| // useful to make another set of tests to test these things in isolation. |
| TEST_F(VisitedLink, TestAddAndQuery) { |
| // init |
| VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(), |
| NULL, true, db_path_, 0); |
| ASSERT_TRUE(master.Init()); |
| |
| PerfTimeLogger timer("Visited_link_add_and_query"); |
| |
| // first check without anything in the table |
| CheckVisited(master, added_prefix, 0, add_count); |
| |
| // now fill half the table |
| const int half_size = add_count / 2; |
| FillTable(master, added_prefix, 0, half_size); |
| |
| // check the table again, half of these URLs will be visited, the other half |
| // will not |
| CheckVisited(master, added_prefix, 0, add_count); |
| |
| // fill the rest of the table |
| FillTable(master, added_prefix, half_size, add_count); |
| |
| // check URLs, doing half visited, half unvisited |
| CheckVisited(master, added_prefix, 0, add_count); |
| CheckVisited(master, unadded_prefix, 0, add_count); |
| } |
| |
| // Tests how long it takes to write and read a large database to and from disk. |
| TEST_F(VisitedLink, TestLoad) { |
| // create a big DB |
| { |
| PerfTimeLogger table_initialization_timer("Table_initialization"); |
| |
| VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(), |
| NULL, true, db_path_, 0); |
| |
| // time init with empty table |
| PerfTimeLogger initTimer("Empty_visited_link_init"); |
| bool success = master.Init(); |
| initTimer.Done(); |
| ASSERT_TRUE(success); |
| |
| // add a bunch of stuff |
| // TODO(maruel): This is very inefficient because the file gets rewritten |
| // many time and this is the actual bottleneck of this test. The file should |
| // only get written that the end of the FillTable call, not 4169(!) times. |
| FillTable(master, added_prefix, 0, load_test_add_count); |
| |
| // time writing the file out out |
| PerfTimeLogger flushTimer("Visited_link_database_flush"); |
| master.RewriteFile(); |
| // TODO(maruel): Without calling FlushFileBuffers(master.file_); you don't |
| // know really how much time it took to write the file. |
| flushTimer.Done(); |
| |
| table_initialization_timer.Done(); |
| } |
| |
| // test loading the DB back, we do this several times since the flushing is |
| // not very reliable. |
| const int load_count = 5; |
| std::vector<double> cold_load_times; |
| std::vector<double> hot_load_times; |
| for (int i = 0; i < load_count; i++) { |
| // make sure the file has to be re-loaded |
| file_util::EvictFileFromSystemCache(db_path_); |
| |
| // cold load (no OS cache, hopefully) |
| { |
| PerfTimer cold_timer; |
| |
| VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(), |
| NULL, |
| true, |
| db_path_, |
| 0); |
| bool success = master.Init(); |
| TimeDelta elapsed = cold_timer.Elapsed(); |
| ASSERT_TRUE(success); |
| |
| cold_load_times.push_back(elapsed.InMillisecondsF()); |
| } |
| |
| // hot load (with OS caching the file in memory) |
| { |
| PerfTimer hot_timer; |
| |
| VisitedLinkMaster master(DummyVisitedLinkEventListener::GetInstance(), |
| NULL, |
| true, |
| db_path_, |
| 0); |
| bool success = master.Init(); |
| TimeDelta elapsed = hot_timer.Elapsed(); |
| ASSERT_TRUE(success); |
| |
| hot_load_times.push_back(elapsed.InMillisecondsF()); |
| } |
| } |
| |
| // We discard the max and return the average time. |
| cold_load_times.erase(std::max_element(cold_load_times.begin(), |
| cold_load_times.end())); |
| hot_load_times.erase(std::max_element(hot_load_times.begin(), |
| hot_load_times.end())); |
| |
| double cold_sum = 0, hot_sum = 0; |
| for (int i = 0; i < static_cast<int>(cold_load_times.size()); i++) { |
| cold_sum += cold_load_times[i]; |
| hot_sum += hot_load_times[i]; |
| } |
| LogPerfResult("Visited_link_cold_load_time", |
| cold_sum / cold_load_times.size(), "ms"); |
| LogPerfResult("Visited_link_hot_load_time", |
| hot_sum / hot_load_times.size(), "ms"); |
| } |