Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / app_list / search / history_unittest.cc
blobd5365e683d9b39c7605e89e4b88e4c0f15f62c06
1 // Copyright 2013 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.
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/test/sequenced_worker_pool_owner.h"
13 #include "base/threading/platform_thread.h"
14 #include "chrome/browser/ui/app_list/search/history_factory.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/app_list/search/dictionary_data_store.h"
17 #include "ui/app_list/search/history.h"
18 #include "ui/app_list/search/history_data.h"
19 #include "ui/app_list/search/history_data_observer.h"
20 #include "ui/app_list/search/history_data_store.h"
22 namespace app_list {
23 namespace test {
25 namespace {
27 const size_t kMaxPrimary = 3;
28 const size_t kMaxSecondary = 2;
30 // HistoryDataLoadWaiter waits for give |data| to be loaded from underlying
31 // store on the blocking pool. The waiter waits on the main message loop until
32 // OnHistoryDataLoadedFromStore() is invoked or the maximum allowed wait time
33 // has passed.
34 class HistoryDataLoadWaiter : public HistoryDataObserver {
35 public:
36 explicit HistoryDataLoadWaiter(HistoryData* data) : data_(data) {}
37 ~HistoryDataLoadWaiter() override {}
39 void Wait() {
40 data_->AddObserver(this);
42 run_loop_.reset(new base::RunLoop);
43 run_loop_->Run();
45 data_->RemoveObserver(this);
48 private:
49 // HistoryDataObserver overrides:
50 void OnHistoryDataLoadedFromStore() override { run_loop_->Quit(); }
52 HistoryData* data_; // Not owned.
53 scoped_ptr<base::RunLoop> run_loop_;
55 DISALLOW_COPY_AND_ASSIGN(HistoryDataLoadWaiter);
58 // StoreFlushWaiter waits for the given |store| to flush its data to disk.
59 // The flush and disk write happens on the blocking pool. The waiter waits
60 // on the main message loop until the OnFlushed() is invoked or the maximum
61 // allowed wait time has passed.
62 class StoreFlushWaiter {
63 public:
64 explicit StoreFlushWaiter(HistoryDataStore* store) : store_(store) {}
65 ~StoreFlushWaiter() {}
67 void Wait() {
68 store_->Flush(
69 base::Bind(&StoreFlushWaiter::OnFlushed, base::Unretained(this)));
71 run_loop_.reset(new base::RunLoop);
72 run_loop_->Run();
75 private:
76 void OnFlushed() {
77 run_loop_->Quit();
80 HistoryDataStore* store_; // Not owned.
81 scoped_ptr<base::RunLoop> run_loop_;
83 DISALLOW_COPY_AND_ASSIGN(StoreFlushWaiter);
86 } // namespace
88 class SearchHistoryTest : public testing::Test {
89 public:
90 SearchHistoryTest() {}
91 ~SearchHistoryTest() override {}
93 // testing::Test overrides:
94 void SetUp() override {
95 worker_pool_owner_.reset(
96 new base::SequencedWorkerPoolOwner(1, "AppLauncherTest"));
97 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
98 CreateHistory();
100 void TearDown() override {
101 Flush();
102 worker_pool_owner_->pool()->Shutdown();
105 void CreateHistory() {
106 const char kStoreDataFileName[] = "app-launcher-test";
107 const base::FilePath data_file =
108 temp_dir_.path().AppendASCII(kStoreDataFileName);
109 scoped_refptr<DictionaryDataStore> dictionary_data_store(
110 new DictionaryDataStore(data_file, worker_pool_owner_->pool().get()));
111 history_.reset(new History(scoped_refptr<HistoryDataStore>(
112 new HistoryDataStore(dictionary_data_store))));
114 // Replace |data_| with test params.
115 history_->data_->RemoveObserver(history_.get());
116 history_->data_.reset(
117 new HistoryData(history_->store_.get(), kMaxPrimary, kMaxSecondary));
118 history_->data_->AddObserver(history_.get());
120 HistoryDataLoadWaiter(history_->data_.get()).Wait();
121 ASSERT_TRUE(history_->IsReady());
124 void Flush() {
125 StoreFlushWaiter(history_->store_.get()).Wait();
128 size_t GetKnownResults(const std::string& query) {
129 known_results_ = history()->GetKnownResults(query).Pass();
130 return known_results_->size();
133 KnownResultType GetResultType(const std::string& result_id) {
134 return known_results_->find(result_id) != known_results_->end()
135 ? (*known_results_.get())[result_id]
136 : UNKNOWN_RESULT;
139 History* history() { return history_.get(); }
140 const HistoryData::Associations& associations() const {
141 return history_->data_->associations();
144 private:
145 base::MessageLoopForUI message_loop_;
146 base::ScopedTempDir temp_dir_;
147 scoped_ptr<base::SequencedWorkerPoolOwner> worker_pool_owner_;
149 scoped_ptr<History> history_;
150 scoped_ptr<KnownResults> known_results_;
152 DISALLOW_COPY_AND_ASSIGN(SearchHistoryTest);
155 TEST_F(SearchHistoryTest, Persistence) {
156 // Ensure it's empty.
157 EXPECT_EQ(0u, GetKnownResults("cal"));
159 // Add one launch event.
160 history()->AddLaunchEvent("cal", "calendar");
161 EXPECT_EQ(1u, GetKnownResults("cal"));
163 // Flush and recreate the history object.
164 Flush();
165 CreateHistory();
167 // History should be initialized with data just added.
168 EXPECT_EQ(1u, GetKnownResults("cal"));
171 TEST_F(SearchHistoryTest, PerfectAndPrefixMatch) {
172 const char kQuery[] = "cal";
173 const char kQueryPrefix[] = "c";
174 const char kPrimary[] = "calendar";
175 const char kSecondary[] = "calculator";
177 history()->AddLaunchEvent(kQuery, kPrimary);
178 history()->AddLaunchEvent(kQuery, kSecondary);
180 EXPECT_EQ(2u, GetKnownResults(kQuery));
181 EXPECT_EQ(PERFECT_PRIMARY, GetResultType(kPrimary));
182 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kSecondary));
184 EXPECT_EQ(2u, GetKnownResults(kQueryPrefix));
185 EXPECT_EQ(PREFIX_PRIMARY, GetResultType(kPrimary));
186 EXPECT_EQ(PREFIX_SECONDARY, GetResultType(kSecondary));
189 TEST_F(SearchHistoryTest, StickyPrimary) {
190 const char kQuery[] = "cal";
191 const char kPrimary[] = "calendar";
192 const char kSecondary[] = "calculator";
193 const char kOther[] = "other";
195 // Add two launch events. kPrimary becomes primary.
196 history()->AddLaunchEvent(kQuery, kPrimary);
197 history()->AddLaunchEvent(kQuery, kSecondary);
199 EXPECT_EQ(2u, GetKnownResults(kQuery));
200 EXPECT_EQ(PERFECT_PRIMARY, GetResultType(kPrimary));
201 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kSecondary));
203 // These launch events should not change primary.
204 history()->AddLaunchEvent(kQuery, kPrimary);
205 history()->AddLaunchEvent(kQuery, kSecondary);
206 history()->AddLaunchEvent(kQuery, kPrimary);
207 history()->AddLaunchEvent(kQuery, kSecondary);
208 history()->AddLaunchEvent(kQuery, kPrimary);
209 history()->AddLaunchEvent(kQuery, kSecondary);
210 history()->AddLaunchEvent(kQuery, kOther);
211 history()->AddLaunchEvent(kQuery, kSecondary);
212 history()->AddLaunchEvent(kQuery, kOther);
213 history()->AddLaunchEvent(kQuery, kSecondary);
214 history()->AddLaunchEvent(kQuery, kOther);
216 EXPECT_EQ(3u, GetKnownResults(kQuery));
217 EXPECT_EQ(PERFECT_PRIMARY, GetResultType(kPrimary));
218 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kSecondary));
219 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kOther));
222 TEST_F(SearchHistoryTest, PromoteSecondary) {
223 const char kQuery[] = "cal";
224 const char kPrimary[] = "calendar";
225 const char kSecondary[] = "calculator";
227 history()->AddLaunchEvent(kQuery, kPrimary);
228 history()->AddLaunchEvent(kQuery, kSecondary);
230 EXPECT_EQ(2u, GetKnownResults(kQuery));
231 EXPECT_EQ(PERFECT_PRIMARY, GetResultType(kPrimary));
232 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kSecondary));
234 // The 2nd launch in a row promotes it to be primary.
235 history()->AddLaunchEvent(kQuery, kSecondary);
237 EXPECT_EQ(2u, GetKnownResults(kQuery));
238 EXPECT_EQ(PERFECT_PRIMARY, GetResultType(kSecondary));
239 EXPECT_EQ(PERFECT_SECONDARY, GetResultType(kPrimary));
242 TEST_F(SearchHistoryTest, MaxPrimary) {
243 for (size_t i = 0; i < kMaxPrimary; ++i) {
244 std::string query = base::SizeTToString(i);
245 history()->AddLaunchEvent(query, "app");
247 EXPECT_EQ(kMaxPrimary, associations().size());
249 // Oldest entries still exists.
250 EXPECT_TRUE(associations().find("0") != associations().end());
251 EXPECT_TRUE(associations().find("1") != associations().end());
253 // Primary entry trimming is based on time. The code could run here too fast
254 // and Time::Now has not changed on some platform. Sleep a bit here to ensure
255 // that some time has passed to get rid of the flake.
256 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(25));
258 // Touches the oldest and 2nd oldest becomes oldest now.
259 history()->AddLaunchEvent("0", "app");
261 // Adds one more
262 history()->AddLaunchEvent("extra", "app");
264 // Number of entries are capped to kMaxPrimary.
265 EXPECT_EQ(kMaxPrimary, associations().size());
267 // Oldest entry is trimmed.
268 EXPECT_FALSE(associations().find("1") != associations().end());
270 // The touched oldest survived.
271 EXPECT_TRUE(associations().find("0") != associations().end());
274 TEST_F(SearchHistoryTest, MaxSecondary) {
275 const char kQuery[] = "query";
276 history()->AddLaunchEvent(kQuery, "primary");
277 for (size_t i = 0; i < kMaxSecondary; ++i) {
278 std::string result_id = base::SizeTToString(i);
279 history()->AddLaunchEvent(kQuery, result_id);
282 EXPECT_EQ(kMaxSecondary + 1, GetKnownResults(kQuery));
283 EXPECT_EQ(PERFECT_SECONDARY, GetResultType("0"));
284 EXPECT_EQ(PERFECT_SECONDARY, GetResultType("1"));
286 // Touches the oldest secondary.
287 history()->AddLaunchEvent(kQuery, "0");
289 // Adds one more.
290 history()->AddLaunchEvent(kQuery, "extra");
292 // Total number of results is capped.
293 EXPECT_EQ(kMaxSecondary + 1, GetKnownResults(kQuery));
295 // The oldest secondary is gone.
296 EXPECT_EQ(UNKNOWN_RESULT, GetResultType("1"));
298 // Touched oldest survived.
299 EXPECT_EQ(PERFECT_SECONDARY, GetResultType("0"));
302 } // namespace test
303 } // namespace app_list