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 #include "chrome/browser/history/android/sqlite_cursor.h" 6 7 #include <jni.h> 8 9 #include "base/android/jni_android.h" 10 #include "base/android/jni_array.h" 11 #include "base/android/jni_string.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/task/cancelable_task_tracker.h" 14 #include "base/time/time.h" 15 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 16 #include "chrome/browser/favicon/chrome_favicon_client.h" 17 #include "chrome/browser/favicon/chrome_favicon_client_factory.h" 18 #include "chrome/browser/favicon/favicon_service.h" 19 #include "chrome/browser/history/android/android_history_provider_service.h" 20 #include "chrome/browser/history/android/android_time.h" 21 #include "chrome/browser/history/history_service.h" 22 #include "chrome/browser/history/history_service_factory.h" 23 #include "chrome/common/chrome_constants.h" 24 #include "chrome/test/base/testing_browser_process.h" 25 #include "chrome/test/base/testing_profile.h" 26 #include "chrome/test/base/testing_profile_manager.h" 27 #include "components/bookmarks/test/bookmark_test_helpers.h" 28 #include "components/history/core/android/android_history_types.h" 29 #include "content/public/browser/browser_thread.h" 30 #include "content/public/test/test_browser_thread.h" 31 #include "content/public/test/test_utils.h" 32 #include "testing/gtest/include/gtest/gtest.h" 33 34 namespace { 35 36 using base::Bind; 37 using base::Time; 38 using content::BrowserThread; 39 using history::AndroidStatement; 40 using history::HistoryAndBookmarkRow; 41 using history::SearchRow; 42 43 // The test cases in this file don't test the JNI interface which will be 44 // covered in Java tests. 45 class SQLiteCursorTest : public testing::Test, 46 public SQLiteCursor::TestObserver { 47 public: 48 SQLiteCursorTest() 49 : profile_manager_( 50 TestingBrowserProcess::GetGlobal()), 51 ui_thread_(BrowserThread::UI, &message_loop_), 52 file_thread_(BrowserThread::FILE, &message_loop_) { 53 } 54 virtual ~SQLiteCursorTest() { 55 } 56 57 protected: 58 virtual void SetUp() OVERRIDE { 59 // Setup the testing profile, so the bookmark_model_sql_handler could 60 // get the bookmark model from it. 61 ASSERT_TRUE(profile_manager_.SetUp()); 62 // It seems that the name has to be chrome::kInitialProfile, so it 63 // could be found by ProfileManager::GetLastUsedProfile(). 64 testing_profile_ = profile_manager_.CreateTestingProfile( 65 chrome::kInitialProfile); 66 67 testing_profile_->CreateBookmarkModel(true); 68 test::WaitForBookmarkModelToLoad( 69 BookmarkModelFactory::GetForProfile(testing_profile_)); 70 71 testing_profile_->CreateFaviconService(); 72 ASSERT_TRUE(testing_profile_->CreateHistoryService(true, false)); 73 service_.reset(new AndroidHistoryProviderService(testing_profile_)); 74 hs_ = HistoryServiceFactory::GetForProfile(testing_profile_, 75 Profile::EXPLICIT_ACCESS); 76 } 77 78 virtual void TearDown() OVERRIDE { 79 testing_profile_->DestroyHistoryService(); 80 profile_manager_.DeleteTestingProfile(chrome::kInitialProfile); 81 testing_profile_ = NULL; 82 } 83 84 // Override SQLiteCursor::TestObserver. 85 virtual void OnPostMoveToTask() OVERRIDE { 86 base::MessageLoop::current()->Run(); 87 } 88 89 virtual void OnGetMoveToResult() OVERRIDE { 90 base::MessageLoop::current()->Quit(); 91 } 92 93 virtual void OnPostGetFaviconTask() OVERRIDE { 94 base::MessageLoop::current()->Run(); 95 } 96 97 virtual void OnGetFaviconResult() OVERRIDE { 98 base::MessageLoop::current()->Quit(); 99 } 100 101 protected: 102 TestingProfileManager profile_manager_; 103 base::MessageLoop message_loop_; 104 content::TestBrowserThread ui_thread_; 105 content::TestBrowserThread file_thread_; 106 scoped_ptr<AndroidHistoryProviderService> service_; 107 base::CancelableTaskTracker cancelable_tracker_; 108 TestingProfile* testing_profile_; 109 HistoryService* hs_; 110 111 112 private: 113 DISALLOW_COPY_AND_ASSIGN(SQLiteCursorTest); 114 }; 115 116 class CallbackHelper : public base::RefCountedThreadSafe<CallbackHelper> { 117 public: 118 CallbackHelper() 119 : success_(false), 120 statement_(NULL) { 121 } 122 123 bool success() const { 124 return success_; 125 } 126 127 AndroidStatement* statement() const { 128 return statement_; 129 } 130 131 void OnInserted(int64 id) { 132 success_ = id != 0; 133 base::MessageLoop::current()->Quit(); 134 } 135 136 void OnQueryResult(AndroidStatement* statement) { 137 success_ = statement != NULL; 138 statement_ = statement; 139 base::MessageLoop::current()->Quit(); 140 } 141 142 private: 143 friend class base::RefCountedThreadSafe<CallbackHelper>; 144 ~CallbackHelper() { 145 } 146 147 bool success_; 148 AndroidStatement* statement_; 149 150 DISALLOW_COPY_AND_ASSIGN(CallbackHelper); 151 }; 152 153 } // namespace 154 155 TEST_F(SQLiteCursorTest, Run) { 156 HistoryAndBookmarkRow row; 157 row.set_raw_url("http://www.google.com/"); 158 row.set_url(GURL("http://www.google.com/")); 159 std::vector<unsigned char> favicon_data; 160 favicon_data.push_back(1); 161 base::RefCountedBytes *data_bytes = 162 base::RefCountedBytes::TakeVector(&favicon_data); 163 row.set_favicon(data_bytes); 164 row.set_last_visit_time(Time::Now()); 165 row.set_visit_count(2); 166 row.set_title(base::UTF8ToUTF16("cnn")); 167 scoped_refptr<CallbackHelper> callback(new CallbackHelper()); 168 169 // Insert a row and verify it succeeded. 170 service_->InsertHistoryAndBookmark( 171 row, 172 Bind(&CallbackHelper::OnInserted, callback.get()), 173 &cancelable_tracker_); 174 175 base::MessageLoop::current()->Run(); 176 EXPECT_TRUE(callback->success()); 177 178 std::vector<HistoryAndBookmarkRow::ColumnID> projections; 179 projections.push_back(HistoryAndBookmarkRow::URL); 180 projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME); 181 projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT); 182 projections.push_back(HistoryAndBookmarkRow::FAVICON); 183 184 // Query the inserted row. 185 service_->QueryHistoryAndBookmarks( 186 projections, 187 std::string(), 188 std::vector<base::string16>(), 189 std::string(), 190 Bind(&CallbackHelper::OnQueryResult, callback.get()), 191 &cancelable_tracker_); 192 base::MessageLoop::current()->Run(); 193 ASSERT_TRUE(callback->success()); 194 195 AndroidStatement* statement = callback->statement(); 196 std::vector<std::string> column_names; 197 column_names.push_back( 198 HistoryAndBookmarkRow::GetAndroidName(HistoryAndBookmarkRow::URL)); 199 column_names.push_back(HistoryAndBookmarkRow::GetAndroidName( 200 HistoryAndBookmarkRow::LAST_VISIT_TIME)); 201 column_names.push_back(HistoryAndBookmarkRow::GetAndroidName( 202 HistoryAndBookmarkRow::VISIT_COUNT)); 203 column_names.push_back(HistoryAndBookmarkRow::GetAndroidName( 204 HistoryAndBookmarkRow::FAVICON)); 205 206 FaviconClient* favicon_client = 207 ChromeFaviconClientFactory::GetForProfile(testing_profile_); 208 FaviconService* favicon_service = 209 new FaviconService(testing_profile_, favicon_client); 210 211 SQLiteCursor* cursor = new SQLiteCursor(column_names, statement, 212 service_.get(), favicon_service); 213 cursor->set_test_observer(this); 214 JNIEnv* env = base::android::AttachCurrentThread(); 215 EXPECT_EQ(1, cursor->GetCount(env, NULL)); 216 EXPECT_EQ(0, cursor->MoveTo(env, NULL, 0)); 217 EXPECT_EQ(row.url().spec(), base::android::ConvertJavaStringToUTF8( 218 cursor->GetString(env, NULL, 0)).c_str()); 219 EXPECT_EQ(history::ToDatabaseTime(row.last_visit_time()), 220 cursor->GetLong(env, NULL, 1)); 221 EXPECT_EQ(row.visit_count(), cursor->GetInt(env, NULL, 2)); 222 base::android::ScopedJavaLocalRef<jbyteArray> data = 223 cursor->GetBlob(env, NULL, 3); 224 std::vector<uint8> out; 225 base::android::JavaByteArrayToByteVector(env, data.obj(), &out); 226 EXPECT_EQ(data_bytes->data().size(), out.size()); 227 EXPECT_EQ(data_bytes->data()[0], out[0]); 228 cursor->Destroy(env, NULL); 229 // Cursor::Destroy posts the task in UI thread, run Message loop to release 230 // the statement, delete SQLiteCursor itself etc. 231 content::RunAllPendingInMessageLoop(); 232 } 233