1 // Copyright (c) 2011 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 "chrome/browser/search_engines/template_url_model_test_util.h" 6 7 #include "base/memory/scoped_temp_dir.h" 8 #include "base/message_loop.h" 9 #include "base/path_service.h" 10 #include "base/threading/thread.h" 11 #include "chrome/browser/search_engines/template_url.h" 12 #include "chrome/browser/search_engines/template_url_model.h" 13 #include "chrome/test/testing_profile.h" 14 #include "content/common/notification_service.h" 15 #include "content/common/notification_type.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 namespace { 19 20 // A Task used to coordinate when the database has finished processing 21 // requests. See note in BlockTillServiceProcessesRequests for details. 22 // 23 // When Run() schedules a QuitTask on the message loop it was created with. 24 class QuitTask2 : public Task { 25 public: 26 QuitTask2() : main_loop_(MessageLoop::current()) {} 27 28 virtual void Run() { 29 main_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 30 } 31 32 private: 33 MessageLoop* main_loop_; 34 }; 35 36 // Blocks the caller until thread has finished servicing all pending 37 // requests. 38 static void WaitForThreadToProcessRequests(BrowserThread::ID identifier) { 39 // Schedule a task on the thread that is processed after all 40 // pending requests on the thread. 41 BrowserThread::PostTask(identifier, FROM_HERE, new QuitTask2()); 42 // Run the current message loop. QuitTask2, when run, invokes Quit, 43 // which unblocks this. 44 MessageLoop::current()->Run(); 45 } 46 47 } // namespace 48 49 // Subclass the TestingProfile so that it can return a WebDataService. 50 class TemplateURLModelTestingProfile : public TestingProfile { 51 public: 52 TemplateURLModelTestingProfile() 53 : TestingProfile(), 54 db_thread_(BrowserThread::DB), 55 io_thread_(BrowserThread::IO) { 56 } 57 58 void SetUp(); 59 void TearDown(); 60 61 // Starts the I/O thread. This isn't done automatically because not every test 62 // needs this. 63 void StartIOThread() { 64 base::Thread::Options options; 65 options.message_loop_type = MessageLoop::TYPE_IO; 66 io_thread_.StartWithOptions(options); 67 } 68 69 virtual WebDataService* GetWebDataService(ServiceAccessType access) { 70 return service_.get(); 71 } 72 73 private: 74 scoped_refptr<WebDataService> service_; 75 ScopedTempDir temp_dir_; 76 BrowserThread db_thread_; 77 BrowserThread io_thread_; 78 }; 79 80 // Trivial subclass of TemplateURLModel that records the last invocation of 81 // SetKeywordSearchTermsForURL. 82 class TestingTemplateURLModel : public TemplateURLModel { 83 public: 84 explicit TestingTemplateURLModel(Profile* profile) 85 : TemplateURLModel(profile) { 86 } 87 88 string16 GetAndClearSearchTerm() { 89 string16 search_term; 90 search_term.swap(search_term_); 91 return search_term; 92 } 93 94 protected: 95 virtual void SetKeywordSearchTermsForURL(const TemplateURL* t_url, 96 const GURL& url, 97 const string16& term) { 98 search_term_ = term; 99 } 100 101 private: 102 string16 search_term_; 103 104 DISALLOW_COPY_AND_ASSIGN(TestingTemplateURLModel); 105 }; 106 107 void TemplateURLModelTestingProfile::SetUp() { 108 db_thread_.Start(); 109 110 // Make unique temp directory. 111 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 112 113 FilePath path = temp_dir_.path().AppendASCII("TestDataService.db"); 114 service_ = new WebDataService; 115 ASSERT_TRUE(service_->InitWithPath(path)); 116 } 117 118 void TemplateURLModelTestingProfile::TearDown() { 119 // Clear the request context so it will get deleted. This should be done 120 // before shutting down the I/O thread to avoid memory leaks. 121 ResetRequestContext(); 122 123 // Wait for the delete of the request context to happen. 124 if (io_thread_.IsRunning()) 125 TemplateURLModelTestUtil::BlockTillIOThreadProcessesRequests(); 126 127 // The I/O thread must be shutdown before the DB thread. 128 io_thread_.Stop(); 129 130 // Clean up the test directory. 131 if (service_.get()) 132 service_->Shutdown(); 133 // Note that we must ensure the DB thread is stopped after WDS 134 // shutdown (so it can commit pending transactions) but before 135 // deleting the test profile directory, otherwise we may not be 136 // able to delete it due to an open transaction. 137 db_thread_.Stop(); 138 } 139 140 TemplateURLModelTestUtil::TemplateURLModelTestUtil() 141 : ui_thread_(BrowserThread::UI, &message_loop_), 142 changed_count_(0) { 143 } 144 145 TemplateURLModelTestUtil::~TemplateURLModelTestUtil() { 146 } 147 148 void TemplateURLModelTestUtil::SetUp() { 149 profile_.reset(new TemplateURLModelTestingProfile()); 150 profile_->SetUp(); 151 profile_->SetTemplateURLModel(new TestingTemplateURLModel(profile_.get())); 152 profile_->GetTemplateURLModel()->AddObserver(this); 153 } 154 155 void TemplateURLModelTestUtil::TearDown() { 156 if (profile_.get()) { 157 profile_->TearDown(); 158 profile_.reset(); 159 } 160 TemplateURLRef::SetGoogleBaseURL(NULL); 161 162 // Flush the message loop to make Purify happy. 163 message_loop_.RunAllPending(); 164 } 165 166 void TemplateURLModelTestUtil::OnTemplateURLModelChanged() { 167 changed_count_++; 168 } 169 170 int TemplateURLModelTestUtil::GetObserverCount() { 171 return changed_count_; 172 } 173 174 void TemplateURLModelTestUtil::ResetObserverCount() { 175 changed_count_ = 0; 176 } 177 178 void TemplateURLModelTestUtil::BlockTillServiceProcessesRequests() { 179 WaitForThreadToProcessRequests(BrowserThread::DB); 180 } 181 182 void TemplateURLModelTestUtil::BlockTillIOThreadProcessesRequests() { 183 WaitForThreadToProcessRequests(BrowserThread::IO); 184 } 185 186 void TemplateURLModelTestUtil::VerifyLoad() { 187 ASSERT_FALSE(model()->loaded()); 188 model()->Load(); 189 BlockTillServiceProcessesRequests(); 190 EXPECT_EQ(1, GetObserverCount()); 191 ResetObserverCount(); 192 } 193 194 void TemplateURLModelTestUtil::ChangeModelToLoadState() { 195 model()->ChangeToLoadedState(); 196 // Initialize the web data service so that the database gets updated with 197 // any changes made. 198 model()->service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); 199 } 200 201 void TemplateURLModelTestUtil::ClearModel() { 202 profile_->SetTemplateURLModel(NULL); 203 } 204 205 void TemplateURLModelTestUtil::ResetModel(bool verify_load) { 206 profile_->SetTemplateURLModel(new TestingTemplateURLModel(profile_.get())); 207 model()->AddObserver(this); 208 changed_count_ = 0; 209 if (verify_load) 210 VerifyLoad(); 211 } 212 213 string16 TemplateURLModelTestUtil::GetAndClearSearchTerm() { 214 return 215 static_cast<TestingTemplateURLModel*>(model())->GetAndClearSearchTerm(); 216 } 217 218 void TemplateURLModelTestUtil::SetGoogleBaseURL( 219 const std::string& base_url) const { 220 TemplateURLRef::SetGoogleBaseURL(new std::string(base_url)); 221 NotificationService::current()->Notify(NotificationType::GOOGLE_URL_UPDATED, 222 NotificationService::AllSources(), 223 NotificationService::NoDetails()); 224 } 225 226 WebDataService* TemplateURLModelTestUtil::GetWebDataService() { 227 return profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); 228 } 229 230 TemplateURLModel* TemplateURLModelTestUtil::model() const { 231 return profile_->GetTemplateURLModel(); 232 } 233 234 TestingProfile* TemplateURLModelTestUtil::profile() const { 235 return profile_.get(); 236 } 237 238 void TemplateURLModelTestUtil::StartIOThread() { 239 profile_->StartIOThread(); 240 } 241