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. 4 5#include <string> 6 7#include "base/memory/scoped_vector.h" 8#include "base/strings/string16.h" 9#include "base/strings/stringprintf.h" 10#include "base/strings/utf_string_conversions.h" 11#include "chrome/browser/ui/app_list/search/chrome_search_result.h" 12#include "chrome/browser/ui/app_list/search/history_types.h" 13#include "chrome/browser/ui/app_list/search/mixer.h" 14#include "chrome/browser/ui/app_list/search/search_provider.h" 15#include "testing/gtest/include/gtest/gtest.h" 16#include "ui/app_list/app_list_model.h" 17 18namespace app_list { 19namespace test { 20 21class TestSearchResult : public ChromeSearchResult { 22 public: 23 TestSearchResult(const std::string& id, double relevance) { 24 set_id(id); 25 set_title(UTF8ToUTF16(id)); 26 set_relevance(relevance); 27 } 28 virtual ~TestSearchResult() {} 29 30 private: 31 // ChromeSearchResult overides: 32 virtual void Open(int event_flags) OVERRIDE {} 33 virtual void InvokeAction(int action_index, int event_flags) OVERRIDE {} 34 virtual scoped_ptr<ChromeSearchResult> Duplicate() OVERRIDE { 35 return scoped_ptr<ChromeSearchResult>( 36 new TestSearchResult(id(), relevance())).Pass(); 37 } 38 virtual ChromeSearchResultType GetType() OVERRIDE { 39 return SEARCH_RESULT_TYPE_BOUNDARY; 40 } 41 42 DISALLOW_COPY_AND_ASSIGN(TestSearchResult); 43}; 44 45class TestSearchProvider : public SearchProvider { 46 public: 47 explicit TestSearchProvider(const std::string& prefix) 48 : prefix_(prefix), 49 count_(0) {} 50 virtual ~TestSearchProvider() {} 51 52 // SearchProvider overrides: 53 virtual void Start(const string16& query) OVERRIDE { 54 ClearResults(); 55 for (size_t i = 0; i < count_; ++i) { 56 const std::string id = 57 base::StringPrintf("%s%d", prefix_.c_str(), static_cast<int>(i)); 58 const double relevance = 1.0 - i / 10.0; 59 Add(scoped_ptr<ChromeSearchResult>( 60 new TestSearchResult(id, relevance)).Pass()); 61 } 62 } 63 virtual void Stop() OVERRIDE {} 64 65 void set_prefix(const std::string& prefix) { prefix_ = prefix; } 66 void set_count(size_t count) { count_ = count; } 67 68 private: 69 std::string prefix_; 70 size_t count_; 71 72 DISALLOW_COPY_AND_ASSIGN(TestSearchProvider); 73}; 74 75class MixerTest : public testing::Test { 76 public: 77 MixerTest() {} 78 virtual ~MixerTest() {} 79 80 // testing::Test overrides: 81 virtual void SetUp() OVERRIDE { 82 results_.reset(new AppListModel::SearchResults); 83 84 providers_.push_back(new TestSearchProvider("app")); 85 providers_.push_back(new TestSearchProvider("omnibox")); 86 providers_.push_back(new TestSearchProvider("webstore")); 87 88 mixer_.reset(new Mixer(results_.get())); 89 mixer_->Init(); 90 mixer_->AddProviderToGroup(Mixer::MAIN_GROUP, providers_[0]); 91 mixer_->AddProviderToGroup(Mixer::OMNIBOX_GROUP, providers_[1]); 92 mixer_->AddProviderToGroup(Mixer::WEBSTORE_GROUP, providers_[2]); 93 } 94 95 void RunQuery() { 96 const string16 query; 97 98 for (size_t i = 0; i < providers_.size(); ++i) { 99 providers_[i]->Start(query); 100 providers_[i]->Stop(); 101 } 102 103 mixer_->MixAndPublish(KnownResults()); 104 } 105 106 std::string GetResults() const { 107 std::string result; 108 for (size_t i = 0; i < results_->item_count(); ++i) { 109 if (!result.empty()) 110 result += ','; 111 112 result += UTF16ToUTF8(results_->GetItemAt(i)->title()); 113 } 114 115 return result; 116 } 117 118 Mixer* mixer() { return mixer_.get(); } 119 TestSearchProvider* app_provider() { return providers_[0]; } 120 TestSearchProvider* omnibox_provider() { return providers_[1]; } 121 TestSearchProvider* webstore_provider() { return providers_[2]; } 122 123 private: 124 scoped_ptr<Mixer> mixer_; 125 scoped_ptr<AppListModel::SearchResults> results_; 126 127 ScopedVector<TestSearchProvider> providers_; 128 129 DISALLOW_COPY_AND_ASSIGN(MixerTest); 130}; 131 132TEST_F(MixerTest, Basic) { 133 struct TestCase { 134 const size_t app_results; 135 const size_t omnibox_results; 136 const size_t webstore_results; 137 const char* expected; 138 } kTestCases[] = { 139 {0, 0, 0, ""}, 140 {4, 6, 2, "app0,app1,app2,app3,omnibox0,webstore0"}, 141 {10, 10, 10, "app0,app1,app2,app3,omnibox0,webstore0"}, 142 {0, 10, 0, "omnibox0,omnibox1,omnibox2,omnibox3,omnibox4,omnibox5"}, 143 {0, 10, 1, "omnibox0,omnibox1,omnibox2,omnibox3,omnibox4,webstore0"}, 144 {0, 10, 2, "omnibox0,omnibox1,omnibox2,omnibox3,webstore0,webstore1"}, 145 {1, 10, 0, "app0,omnibox0,omnibox1,omnibox2,omnibox3,omnibox4"}, 146 {2, 10, 0, "app0,app1,omnibox0,omnibox1,omnibox2,omnibox3"}, 147 {2, 10, 1, "app0,app1,omnibox0,omnibox1,omnibox2,webstore0"}, 148 {2, 10, 2, "app0,app1,omnibox0,omnibox1,webstore0,webstore1"}, 149 {2, 0, 2, "app0,app1,webstore0,webstore1"}, 150 {0, 0, 0, ""}, 151 }; 152 153 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 154 app_provider()->set_count(kTestCases[i].app_results); 155 omnibox_provider()->set_count(kTestCases[i].omnibox_results); 156 webstore_provider()->set_count(kTestCases[i].webstore_results); 157 RunQuery(); 158 159 EXPECT_EQ(kTestCases[i].expected, GetResults()) << "Case " << i; 160 } 161} 162 163TEST_F(MixerTest, RemoveDuplicates) { 164 const std::string dup = "dup"; 165 166 // This gives "dup0,dup1,dup2". 167 app_provider()->set_prefix(dup); 168 app_provider()->set_count(3); 169 170 // This gives "dup0,dup1". 171 omnibox_provider()->set_prefix(dup); 172 omnibox_provider()->set_count(2); 173 174 // This gives "dup0". 175 webstore_provider()->set_prefix(dup); 176 webstore_provider()->set_count(1); 177 178 RunQuery(); 179 180 // Only three results with unique id are kept. 181 EXPECT_EQ("dup0,dup1,dup2", GetResults()); 182} 183 184} // namespace test 185} // namespace app_list 186