Standardize usage of virtual/override/final in chrome/browser/ui/
[chromium-blink-merge.git] / chrome / browser / ui / webui / ntp / suggestions_combiner_unittest.cc
blob1a14aaf2e0024347e9e770ef8cc63d7402c7aa66
1 // Copyright (c) 2012 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 // TODO(beaudoin): What is really needed here?
7 #include <deque>
8 #include <string>
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chrome/browser/ui/webui/ntp/suggestions_combiner.h"
15 #include "chrome/browser/ui/webui/ntp/suggestions_page_handler.h"
16 #include "chrome/browser/ui/webui/ntp/suggestions_source.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace {
22 struct SourceInfo {
23 int weight;
24 const char* source_name;
25 int number_of_suggestions;
28 struct TestDescription {
29 SourceInfo sources[3];
30 const char* results[8];
31 } test_suite[] = {
32 // One source, more than 8 items.
34 {{1, "A", 10}},
35 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"}
37 // One source, exactly 8 items.
39 {{1, "A", 8}},
40 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"}
42 // One source, not enough items.
44 {{1, "A", 3}},
45 {"A 0", "A 1", "A 2"}
47 // One source, no items.
49 {{1, "A", 0}},
52 // Two sources, equal weight, more than 8 items.
54 {{1, "A", 10}, {1, "B", 10}},
55 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2", "B 3"}
57 // Two sources, equal weight, exactly 8 items.
59 {{1, "A", 4}, {1, "B", 4}},
60 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2", "B 3"}
62 // Two sources, equal weight, exactly 8 items but source A has more.
64 {{1, "A", 5}, {1, "B", 3}},
65 {"A 0", "A 1", "A 2", "A 3", "A 4", "B 0", "B 1", "B 2"}
67 // Two sources, equal weight, exactly 8 items but source B has more.
69 {{1, "A", 2}, {1, "B", 6}},
70 {"A 0", "A 1", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5"}
72 // Two sources, equal weight, exactly 8 items but source A has none.
74 {{1, "A", 0}, {1, "B", 8}},
75 {"B 0", "B 1", "B 2", "B 3", "B 4", "B 5", "B 6", "B 7"}
77 // Two sources, equal weight, exactly 8 items but source B has none.
79 {{1, "A", 8}, {1, "B", 0}},
80 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"}
82 // Two sources, equal weight, less than 8 items.
84 {{1, "A", 3}, {1, "B", 3}},
85 {"A 0", "A 1", "A 2", "B 0", "B 1", "B 2"}
87 // Two sources, equal weight, less than 8 items but source A has more.
89 {{1, "A", 4}, {1, "B", 3}},
90 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2"}
92 // Two sources, equal weight, less than 8 items but source B has more.
94 {{1, "A", 1}, {1, "B", 3}},
95 {"A 0", "B 0", "B 1", "B 2"}
97 // Two sources, weights 3/4 A 1/4 B, more than 8 items.
99 {{3, "A", 10}, {1, "B", 10}},
100 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "B 0", "B 1"}
102 // Two sources, weights 1/8 A 7/8 B, more than 8 items.
104 {{1, "A", 10}, {7, "B", 10}},
105 {"A 0", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5", "B 6"}
107 // Two sources, weights 1/3 A 2/3 B, more than 8 items.
109 {{1, "A", 10}, {2, "B", 10}},
110 {"A 0", "A 1", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5"}
112 // Three sources, weights 1/2 A 1/4 B 1/4 C, more than 8 items.
114 {{2, "A", 10}, {1, "B", 10}, {1, "C", 10}},
115 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "C 0", "C 1"}
117 // Three sources, weights 1/3 A 1/3 B 1/3 C, more than 8 items.
119 {{1, "A", 10}, {1, "B", 10}, {1, "C", 10}},
120 {"A 0", "A 1", "B 0", "B 1", "B 2", "C 0", "C 1", "C 2"}
122 // Extra items should be grouped together.
124 {{1, "A", 3}, {1, "B", 4}, {10, "C", 1}},
125 {"A 0", "A 1", "A 2", "B 0", "B 1", "B 2", "B 3", "C 0"}
129 } // namespace
131 // Stub for a SuggestionsSource that can provide a number of fake suggestions.
132 // Fake suggestions are DictionaryValue with a single "title" string field
133 // containing the |source_name| followed by the index of the suggestion.
134 // Not in the empty namespace since it's a friend of SuggestionsCombiner.
135 class SuggestionsSourceStub : public SuggestionsSource {
136 public:
137 explicit SuggestionsSourceStub(int weight,
138 const std::string& source_name, int number_of_suggestions)
139 : combiner_(NULL),
140 weight_(weight),
141 source_name_(source_name),
142 number_of_suggestions_(number_of_suggestions),
143 debug_(false) {
145 ~SuggestionsSourceStub() override { STLDeleteElements(&items_); }
147 // Call this method to simulate that the SuggestionsSource has received all
148 // its suggestions.
149 void Done() {
150 combiner_->OnItemsReady();
153 private:
154 // SuggestionsSource Override and implementation.
155 void SetDebug(bool enable) override { debug_ = enable; }
156 int GetWeight() override { return weight_; }
157 int GetItemCount() override { return items_.size(); }
158 base::DictionaryValue* PopItem() override {
159 if (items_.empty())
160 return NULL;
161 base::DictionaryValue* item = items_.front();
162 items_.pop_front();
163 return item;
166 void FetchItems(Profile* profile) override {
167 char num_str[21]; // Enough to hold all numbers up to 64-bits.
168 for (int i = 0; i < number_of_suggestions_; ++i) {
169 base::snprintf(num_str, sizeof(num_str), "%d", i);
170 AddSuggestion(source_name_ + ' ' + num_str);
174 // Adds a fake suggestion. This suggestion is a DictionaryValue with a single
175 // "title" field containing |title|.
176 void AddSuggestion(const std::string& title) {
177 base::DictionaryValue* item = new base::DictionaryValue();
178 item->SetString("title", title);
179 items_.push_back(item);
182 void SetCombiner(SuggestionsCombiner* combiner) override {
183 DCHECK(!combiner_);
184 combiner_ = combiner;
187 // Our combiner.
188 SuggestionsCombiner* combiner_;
190 int weight_;
191 std::string source_name_;
192 int number_of_suggestions_;
193 bool debug_;
195 // Keep the results of the db query here.
196 std::deque<base::DictionaryValue*> items_;
198 DISALLOW_COPY_AND_ASSIGN(SuggestionsSourceStub);
201 class SuggestionsCombinerTest : public testing::Test {
202 public:
203 SuggestionsCombinerTest() {
206 protected:
207 Profile* profile_;
208 SuggestionsHandler* suggestions_handler_;
209 SuggestionsCombiner* combiner_;
211 void Reset() {
212 delete combiner_;
213 combiner_ = new SuggestionsCombiner(suggestions_handler_, profile_);
216 private:
217 virtual void SetUp() {
218 profile_ = new TestingProfile();
219 suggestions_handler_ = new SuggestionsHandler();
220 combiner_ = new SuggestionsCombiner(suggestions_handler_, profile_);
223 virtual void TearDown() {
224 delete combiner_;
225 delete suggestions_handler_;
226 delete profile_;
229 DISALLOW_COPY_AND_ASSIGN(SuggestionsCombinerTest);
232 TEST_F(SuggestionsCombinerTest, NoSource) {
233 combiner_->FetchItems(NULL);
234 EXPECT_EQ(0UL, combiner_->GetPageValues()->GetSize());
237 TEST_F(SuggestionsCombinerTest, SourcesAreNotDoneFetching) {
238 combiner_->AddSource(new SuggestionsSourceStub(1, "sourceA", 10));
239 combiner_->AddSource(new SuggestionsSourceStub(1, "sourceB", 10));
240 combiner_->FetchItems(NULL);
241 EXPECT_EQ(0UL, combiner_->GetPageValues()->GetSize());
244 TEST_F(SuggestionsCombinerTest, TestSuite) {
245 size_t test_count = arraysize(test_suite);
246 for (size_t i = 0; i < test_count; ++i) {
247 const TestDescription& description = test_suite[i];
248 size_t source_count = arraysize(description.sources);
250 scoped_ptr<SuggestionsSourceStub*[]> sources(
251 new SuggestionsSourceStub*[source_count]);
253 // Setup sources.
254 for (size_t j = 0; j < source_count; ++j) {
255 const SourceInfo& source_info = description.sources[j];
256 // A NULL |source_name| means we shouldn't add this source.
257 if (source_info.source_name) {
258 sources[j] = new SuggestionsSourceStub(source_info.weight,
259 source_info.source_name, source_info.number_of_suggestions);
260 combiner_->AddSource(sources[j]);
261 } else {
262 sources[j] = NULL;
266 // Start fetching.
267 combiner_->FetchItems(NULL);
269 // Sources complete.
270 for (size_t j = 0; j < source_count; ++j) {
271 if (sources[j])
272 sources[j]->Done();
275 // Verify expectations.
276 base::ListValue* results = combiner_->GetPageValues();
277 size_t result_count = results->GetSize();
278 EXPECT_LE(result_count, 8UL);
279 for (size_t j = 0; j < 8; ++j) {
280 if (j < result_count) {
281 std::string value;
282 base::DictionaryValue* dictionary;
283 results->GetDictionary(j, &dictionary);
284 dictionary->GetString("title", &value);
285 EXPECT_STREQ(description.results[j], value.c_str()) <<
286 " test index:" << i;
287 } else {
288 EXPECT_EQ(description.results[j], static_cast<const char*>(NULL)) <<
289 " test index:" << i;
293 Reset();