| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 // TODO(beaudoin): What is really needed here? | |
| 6 | |
| 7 #include <deque> | |
| 8 #include <string> | |
| 9 | |
| 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 "content/public/test/test_browser_thread_bundle.h" | |
| 19 #include "testing/gtest/include/gtest/gtest.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 struct SourceInfo { | |
| 24 int weight; | |
| 25 const char* source_name; | |
| 26 int number_of_suggestions; | |
| 27 }; | |
| 28 | |
| 29 struct TestDescription { | |
| 30 SourceInfo sources[3]; | |
| 31 const char* results[8]; | |
| 32 } test_suite[] = { | |
| 33 // One source, more than 8 items. | |
| 34 { | |
| 35 {{1, "A", 10}}, | |
| 36 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"} | |
| 37 }, | |
| 38 // One source, exactly 8 items. | |
| 39 { | |
| 40 {{1, "A", 8}}, | |
| 41 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"} | |
| 42 }, | |
| 43 // One source, not enough items. | |
| 44 { | |
| 45 {{1, "A", 3}}, | |
| 46 {"A 0", "A 1", "A 2"} | |
| 47 }, | |
| 48 // One source, no items. | |
| 49 { | |
| 50 {{1, "A", 0}}, | |
| 51 {} | |
| 52 }, | |
| 53 // Two sources, equal weight, more than 8 items. | |
| 54 { | |
| 55 {{1, "A", 10}, {1, "B", 10}}, | |
| 56 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2", "B 3"} | |
| 57 }, | |
| 58 // Two sources, equal weight, exactly 8 items. | |
| 59 { | |
| 60 {{1, "A", 4}, {1, "B", 4}}, | |
| 61 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2", "B 3"} | |
| 62 }, | |
| 63 // Two sources, equal weight, exactly 8 items but source A has more. | |
| 64 { | |
| 65 {{1, "A", 5}, {1, "B", 3}}, | |
| 66 {"A 0", "A 1", "A 2", "A 3", "A 4", "B 0", "B 1", "B 2"} | |
| 67 }, | |
| 68 // Two sources, equal weight, exactly 8 items but source B has more. | |
| 69 { | |
| 70 {{1, "A", 2}, {1, "B", 6}}, | |
| 71 {"A 0", "A 1", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5"} | |
| 72 }, | |
| 73 // Two sources, equal weight, exactly 8 items but source A has none. | |
| 74 { | |
| 75 {{1, "A", 0}, {1, "B", 8}}, | |
| 76 {"B 0", "B 1", "B 2", "B 3", "B 4", "B 5", "B 6", "B 7"} | |
| 77 }, | |
| 78 // Two sources, equal weight, exactly 8 items but source B has none. | |
| 79 { | |
| 80 {{1, "A", 8}, {1, "B", 0}}, | |
| 81 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "A 6", "A 7"} | |
| 82 }, | |
| 83 // Two sources, equal weight, less than 8 items. | |
| 84 { | |
| 85 {{1, "A", 3}, {1, "B", 3}}, | |
| 86 {"A 0", "A 1", "A 2", "B 0", "B 1", "B 2"} | |
| 87 }, | |
| 88 // Two sources, equal weight, less than 8 items but source A has more. | |
| 89 { | |
| 90 {{1, "A", 4}, {1, "B", 3}}, | |
| 91 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "B 2"} | |
| 92 }, | |
| 93 // Two sources, equal weight, less than 8 items but source B has more. | |
| 94 { | |
| 95 {{1, "A", 1}, {1, "B", 3}}, | |
| 96 {"A 0", "B 0", "B 1", "B 2"} | |
| 97 }, | |
| 98 // Two sources, weights 3/4 A 1/4 B, more than 8 items. | |
| 99 { | |
| 100 {{3, "A", 10}, {1, "B", 10}}, | |
| 101 {"A 0", "A 1", "A 2", "A 3", "A 4", "A 5", "B 0", "B 1"} | |
| 102 }, | |
| 103 // Two sources, weights 1/8 A 7/8 B, more than 8 items. | |
| 104 { | |
| 105 {{1, "A", 10}, {7, "B", 10}}, | |
| 106 {"A 0", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5", "B 6"} | |
| 107 }, | |
| 108 // Two sources, weights 1/3 A 2/3 B, more than 8 items. | |
| 109 { | |
| 110 {{1, "A", 10}, {2, "B", 10}}, | |
| 111 {"A 0", "A 1", "B 0", "B 1", "B 2", "B 3", "B 4", "B 5"} | |
| 112 }, | |
| 113 // Three sources, weights 1/2 A 1/4 B 1/4 C, more than 8 items. | |
| 114 { | |
| 115 {{2, "A", 10}, {1, "B", 10}, {1, "C", 10}}, | |
| 116 {"A 0", "A 1", "A 2", "A 3", "B 0", "B 1", "C 0", "C 1"} | |
| 117 }, | |
| 118 // Three sources, weights 1/3 A 1/3 B 1/3 C, more than 8 items. | |
| 119 { | |
| 120 {{1, "A", 10}, {1, "B", 10}, {1, "C", 10}}, | |
| 121 {"A 0", "A 1", "B 0", "B 1", "B 2", "C 0", "C 1", "C 2"} | |
| 122 }, | |
| 123 // Extra items should be grouped together. | |
| 124 { | |
| 125 {{1, "A", 3}, {1, "B", 4}, {10, "C", 1}}, | |
| 126 {"A 0", "A 1", "A 2", "B 0", "B 1", "B 2", "B 3", "C 0"} | |
| 127 } | |
| 128 }; | |
| 129 | |
| 130 } // namespace | |
| 131 | |
| 132 // Stub for a SuggestionsSource that can provide a number of fake suggestions. | |
| 133 // Fake suggestions are DictionaryValue with a single "title" string field | |
| 134 // containing the |source_name| followed by the index of the suggestion. | |
| 135 // Not in the empty namespace since it's a friend of SuggestionsCombiner. | |
| 136 class SuggestionsSourceStub : public SuggestionsSource { | |
| 137 public: | |
| 138 explicit SuggestionsSourceStub(int weight, | |
| 139 const std::string& source_name, int number_of_suggestions) | |
| 140 : combiner_(NULL), | |
| 141 weight_(weight), | |
| 142 source_name_(source_name), | |
| 143 number_of_suggestions_(number_of_suggestions), | |
| 144 debug_(false) { | |
| 145 } | |
| 146 ~SuggestionsSourceStub() override { STLDeleteElements(&items_); } | |
| 147 | |
| 148 // Call this method to simulate that the SuggestionsSource has received all | |
| 149 // its suggestions. | |
| 150 void Done() { | |
| 151 combiner_->OnItemsReady(); | |
| 152 } | |
| 153 | |
| 154 private: | |
| 155 // SuggestionsSource Override and implementation. | |
| 156 void SetDebug(bool enable) override { debug_ = enable; } | |
| 157 int GetWeight() override { return weight_; } | |
| 158 int GetItemCount() override { return items_.size(); } | |
| 159 base::DictionaryValue* PopItem() override { | |
| 160 if (items_.empty()) | |
| 161 return NULL; | |
| 162 base::DictionaryValue* item = items_.front(); | |
| 163 items_.pop_front(); | |
| 164 return item; | |
| 165 } | |
| 166 | |
| 167 void FetchItems(Profile* profile) override { | |
| 168 char num_str[21]; // Enough to hold all numbers up to 64-bits. | |
| 169 for (int i = 0; i < number_of_suggestions_; ++i) { | |
| 170 base::snprintf(num_str, sizeof(num_str), "%d", i); | |
| 171 AddSuggestion(source_name_ + ' ' + num_str); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 // Adds a fake suggestion. This suggestion is a DictionaryValue with a single | |
| 176 // "title" field containing |title|. | |
| 177 void AddSuggestion(const std::string& title) { | |
| 178 base::DictionaryValue* item = new base::DictionaryValue(); | |
| 179 item->SetString("title", title); | |
| 180 items_.push_back(item); | |
| 181 } | |
| 182 | |
| 183 void SetCombiner(SuggestionsCombiner* combiner) override { | |
| 184 DCHECK(!combiner_); | |
| 185 combiner_ = combiner; | |
| 186 } | |
| 187 | |
| 188 // Our combiner. | |
| 189 SuggestionsCombiner* combiner_; | |
| 190 | |
| 191 int weight_; | |
| 192 std::string source_name_; | |
| 193 int number_of_suggestions_; | |
| 194 bool debug_; | |
| 195 | |
| 196 // Keep the results of the db query here. | |
| 197 std::deque<base::DictionaryValue*> items_; | |
| 198 | |
| 199 DISALLOW_COPY_AND_ASSIGN(SuggestionsSourceStub); | |
| 200 }; | |
| 201 | |
| 202 class SuggestionsCombinerTest : public testing::Test { | |
| 203 public: | |
| 204 SuggestionsCombinerTest() { | |
| 205 } | |
| 206 | |
| 207 protected: | |
| 208 content::TestBrowserThreadBundle thread_bundle_; | |
| 209 Profile* profile_; | |
| 210 SuggestionsHandler* suggestions_handler_; | |
| 211 SuggestionsCombiner* combiner_; | |
| 212 | |
| 213 void Reset() { | |
| 214 delete combiner_; | |
| 215 combiner_ = new SuggestionsCombiner(suggestions_handler_, profile_); | |
| 216 } | |
| 217 | |
| 218 private: | |
| 219 void SetUp() override { | |
| 220 profile_ = new TestingProfile(); | |
| 221 suggestions_handler_ = new SuggestionsHandler(); | |
| 222 combiner_ = new SuggestionsCombiner(suggestions_handler_, profile_); | |
| 223 } | |
| 224 | |
| 225 void TearDown() override { | |
| 226 delete combiner_; | |
| 227 delete suggestions_handler_; | |
| 228 delete profile_; | |
| 229 } | |
| 230 | |
| 231 DISALLOW_COPY_AND_ASSIGN(SuggestionsCombinerTest); | |
| 232 }; | |
| 233 | |
| 234 TEST_F(SuggestionsCombinerTest, NoSource) { | |
| 235 combiner_->FetchItems(NULL); | |
| 236 EXPECT_EQ(0UL, combiner_->GetPageValues()->GetSize()); | |
| 237 } | |
| 238 | |
| 239 TEST_F(SuggestionsCombinerTest, SourcesAreNotDoneFetching) { | |
| 240 combiner_->AddSource(new SuggestionsSourceStub(1, "sourceA", 10)); | |
| 241 combiner_->AddSource(new SuggestionsSourceStub(1, "sourceB", 10)); | |
| 242 combiner_->FetchItems(NULL); | |
| 243 EXPECT_EQ(0UL, combiner_->GetPageValues()->GetSize()); | |
| 244 } | |
| 245 | |
| 246 TEST_F(SuggestionsCombinerTest, TestSuite) { | |
| 247 size_t test_count = arraysize(test_suite); | |
| 248 for (size_t i = 0; i < test_count; ++i) { | |
| 249 const TestDescription& description = test_suite[i]; | |
| 250 size_t source_count = arraysize(description.sources); | |
| 251 | |
| 252 scoped_ptr<SuggestionsSourceStub*[]> sources( | |
| 253 new SuggestionsSourceStub*[source_count]); | |
| 254 | |
| 255 // Setup sources. | |
| 256 for (size_t j = 0; j < source_count; ++j) { | |
| 257 const SourceInfo& source_info = description.sources[j]; | |
| 258 // A NULL |source_name| means we shouldn't add this source. | |
| 259 if (source_info.source_name) { | |
| 260 sources[j] = new SuggestionsSourceStub(source_info.weight, | |
| 261 source_info.source_name, source_info.number_of_suggestions); | |
| 262 combiner_->AddSource(sources[j]); | |
| 263 } else { | |
| 264 sources[j] = NULL; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 // Start fetching. | |
| 269 combiner_->FetchItems(NULL); | |
| 270 | |
| 271 // Sources complete. | |
| 272 for (size_t j = 0; j < source_count; ++j) { | |
| 273 if (sources[j]) | |
| 274 sources[j]->Done(); | |
| 275 } | |
| 276 | |
| 277 // Verify expectations. | |
| 278 base::ListValue* results = combiner_->GetPageValues(); | |
| 279 size_t result_count = results->GetSize(); | |
| 280 EXPECT_LE(result_count, 8UL); | |
| 281 for (size_t j = 0; j < 8; ++j) { | |
| 282 if (j < result_count) { | |
| 283 std::string value; | |
| 284 base::DictionaryValue* dictionary; | |
| 285 results->GetDictionary(j, &dictionary); | |
| 286 dictionary->GetString("title", &value); | |
| 287 EXPECT_STREQ(description.results[j], value.c_str()) << | |
| 288 " test index:" << i; | |
| 289 } else { | |
| 290 EXPECT_EQ(description.results[j], static_cast<const char*>(NULL)) << | |
| 291 " test index:" << i; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 Reset(); | |
| 296 } | |
| 297 } | |
| 298 | |
| OLD | NEW |