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 <limits> 6 #include <string> 7 8 #include "app/sql/statement.h" 9 #include "base/file_util.h" 10 #include "base/memory/scoped_temp_dir.h" 11 #include "base/utf_string_conversions.h" 12 #include "build/build_config.h" 13 #include "chrome/browser/password_manager/encryptor.h" 14 #include "chrome/browser/sync/syncable/directory_manager.h" 15 #include "chrome/browser/sync/util/user_settings.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 using std::numeric_limits; 19 20 namespace { 21 22 const FilePath::CharType kV10UserSettingsDB[] = 23 FILE_PATH_LITERAL("Version10Settings.sqlite3"); 24 const FilePath::CharType kV11UserSettingsDB[] = 25 FILE_PATH_LITERAL("Version11Settings.sqlite3"); 26 const FilePath::CharType kOldStyleSyncDataDB[] = 27 FILE_PATH_LITERAL("OldStyleSyncData.sqlite3"); 28 29 } // namespace 30 31 class UserSettingsTest : public testing::Test { 32 public: 33 UserSettingsTest() : sync_data_("Some sync data") {} 34 35 virtual void SetUp() { 36 #if defined(OS_MACOSX) 37 // Need to mock the Keychain for unit tests on Mac to avoid possible 38 // blocking UI. |SetAuthTokenForService| uses Encryptor. 39 Encryptor::UseMockKeychain(true); 40 #endif 41 } 42 43 // Creates and populates the V10 database files within 44 // |destination_directory|. 45 void SetUpVersion10Databases(const FilePath& destination_directory) { 46 v10_user_setting_db_path_ = 47 destination_directory.Append(FilePath(kV10UserSettingsDB)); 48 49 sql::Connection db; 50 ASSERT_TRUE(db.Open(v10_user_setting_db_path_)); 51 52 old_style_sync_data_path_ = 53 destination_directory.Append(FilePath(kOldStyleSyncDataDB)); 54 55 ASSERT_EQ(sync_data_.length(), 56 static_cast<size_t>(file_util::WriteFile( 57 old_style_sync_data_path_, sync_data_.data(), 58 sync_data_.length()))); 59 60 // Create settings table. 61 ASSERT_TRUE(db.Execute( 62 "CREATE TABLE settings (email, key, value, PRIMARY KEY(email, key)" 63 " ON CONFLICT REPLACE)")); 64 65 // Add a blank signin table. 66 ASSERT_TRUE(db.Execute( 67 "CREATE TABLE signin_types (signin, signin_type)")); 68 69 // Create and populate version table. 70 ASSERT_TRUE(db.Execute("CREATE TABLE db_version (version)")); 71 { 72 const char* query = "INSERT INTO db_version VALUES(?)"; 73 sql::Statement s(db.GetUniqueStatement(query)); 74 if (!s) 75 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 76 77 s.BindInt(0, 10); 78 if (!s.Run()) 79 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 80 } 81 82 // Create shares table. 83 ASSERT_TRUE(db.Execute( 84 "CREATE TABLE shares (email, share_name, file_name," 85 " PRIMARY KEY(email, share_name) ON CONFLICT REPLACE)")); 86 // Populate a share. 87 { 88 const char* query = "INSERT INTO shares VALUES(?, ?, ?)"; 89 sql::Statement s(db.GetUniqueStatement(query)); 90 if (!s) 91 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 92 93 s.BindString(0, "foo (at) foo.com"); 94 s.BindString(1, "foo (at) foo.com"); 95 #if defined(OS_WIN) 96 s.BindString(2, WideToUTF8(old_style_sync_data_path_.value())); 97 #elif defined(OS_POSIX) 98 s.BindString(2, old_style_sync_data_path_.value()); 99 #endif 100 if (!s.Run()) 101 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 102 } 103 } 104 105 // Creates and populates the V11 database file within 106 // |destination_directory|. 107 void SetUpVersion11Database(const FilePath& destination_directory) { 108 v11_user_setting_db_path_ = 109 destination_directory.Append(FilePath(kV11UserSettingsDB)); 110 111 sql::Connection db; 112 ASSERT_TRUE(db.Open(v11_user_setting_db_path_)); 113 114 // Create settings table. 115 ASSERT_TRUE(db.Execute( 116 "CREATE TABLE settings (email, key, value, PRIMARY KEY(email, key)" 117 " ON CONFLICT REPLACE)")); 118 119 // Create and populate version table. 120 ASSERT_TRUE(db.Execute("CREATE TABLE db_version (version)")); 121 { 122 const char* query = "INSERT INTO db_version VALUES(?)"; 123 sql::Statement s(db.GetUniqueStatement(query)); 124 if (!s) 125 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 126 127 s.BindInt(0, 11); 128 if (!s.Run()) 129 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 130 } 131 132 ASSERT_TRUE(db.Execute( 133 "CREATE TABLE signin_types (signin, signin_type)")); 134 { 135 const char* query = "INSERT INTO signin_types VALUES(?, ?)"; 136 sql::Statement s(db.GetUniqueStatement(query)); 137 if (!s) 138 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 139 140 s.BindString(0, "test"); 141 s.BindString(1, "test"); 142 if (!s.Run()) 143 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 144 } 145 } 146 147 const std::string& sync_data() const { return sync_data_; } 148 const FilePath& v10_user_setting_db_path() const { 149 return v10_user_setting_db_path_; 150 } 151 const FilePath& v11_user_setting_db_path() const { 152 return v11_user_setting_db_path_; 153 } 154 const FilePath& old_style_sync_data_path() const { 155 return old_style_sync_data_path_; 156 } 157 158 private: 159 FilePath v10_user_setting_db_path_; 160 FilePath old_style_sync_data_path_; 161 162 FilePath v11_user_setting_db_path_; 163 164 std::string sync_data_; 165 }; 166 167 TEST_F(UserSettingsTest, MigrateFromV10ToV11) { 168 ScopedTempDir temp_dir; 169 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 170 SetUpVersion10Databases(temp_dir.path()); 171 { 172 // Create a UserSettings, which should trigger migration code. We do this 173 // inside a scoped block so it closes itself and we can poke around to see 174 // what happened later. 175 browser_sync::UserSettings settings; 176 settings.Init(v10_user_setting_db_path()); 177 } 178 179 // Now poke around using sqlite to see if UserSettings migrated properly. 180 sql::Connection db; 181 ASSERT_TRUE(db.Open(v10_user_setting_db_path())); 182 183 // Note that we don't use ScopedStatement to avoid closing the sqlite handle 184 // before finalizing the statement. 185 { 186 const char* query = "SELECT version FROM db_version"; 187 sql::Statement version_query(db.GetUniqueStatement(query)); 188 if (!version_query) 189 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 190 191 ASSERT_TRUE(version_query.Step()); 192 const int version = version_query.ColumnInt(0); 193 EXPECT_GE(version, 11); 194 } 195 196 EXPECT_FALSE(file_util::PathExists(old_style_sync_data_path())); 197 198 FilePath new_style_path = temp_dir.path().Append( 199 syncable::DirectoryManager::GetSyncDataDatabaseFilename()); 200 201 std::string contents; 202 ASSERT_TRUE(file_util::ReadFileToString(new_style_path, &contents)); 203 EXPECT_TRUE(sync_data() == contents); 204 } 205 206 TEST_F(UserSettingsTest, MigrateFromV11ToV12) { 207 ScopedTempDir temp_dir; 208 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 209 SetUpVersion11Database(temp_dir.path()); 210 { 211 browser_sync::UserSettings settings; 212 settings.Init(v11_user_setting_db_path()); 213 } 214 sql::Connection db; 215 ASSERT_TRUE(db.Open(v11_user_setting_db_path())); 216 217 { 218 const char* query = "SELECT version FROM db_version"; 219 sql::Statement version_query(db.GetUniqueStatement(query)); 220 if (!version_query) 221 LOG(FATAL) << query << "\n" << db.GetErrorMessage(); 222 223 ASSERT_TRUE(version_query.Step()); 224 const int version = version_query.ColumnInt(0); 225 EXPECT_GE(version, 12); 226 227 const char* query2 = "SELECT name FROM sqlite_master " 228 "WHERE type='table' AND name='signin_types'"; 229 sql::Statement table_query(db.GetUniqueStatement(query2)); 230 if (!table_query) 231 LOG(FATAL) << query2 << "\n" << db.GetErrorMessage(); 232 233 ASSERT_FALSE(table_query.Step()); 234 } 235 } 236 237 TEST_F(UserSettingsTest, APEncode) { 238 std::string test; 239 char i; 240 for (i = numeric_limits<char>::min(); i < numeric_limits<char>::max(); ++i) 241 test.push_back(i); 242 test.push_back(i); 243 const std::string encoded = browser_sync::APEncode(test); 244 const std::string decoded = browser_sync::APDecode(encoded); 245 ASSERT_EQ(test, decoded); 246 } 247 248 TEST_F(UserSettingsTest, PersistEmptyToken) { 249 ScopedTempDir temp_dir; 250 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 251 browser_sync::UserSettings settings; 252 settings.Init(temp_dir.path().AppendASCII("UserSettings.sqlite3")); 253 settings.SetAuthTokenForService("username", "service", ""); 254 std::string username; 255 std::string token; 256 ASSERT_TRUE(settings.GetLastUserAndServiceToken("service", &username, 257 &token)); 258 EXPECT_EQ("", token); 259 EXPECT_EQ("username", username); 260 } 261 262 TEST_F(UserSettingsTest, PersistNonEmptyToken) { 263 ScopedTempDir temp_dir; 264 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 265 browser_sync::UserSettings settings; 266 settings.Init(temp_dir.path().AppendASCII("UserSettings.sqlite3")); 267 settings.SetAuthTokenForService("username", "service", 268 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah" 269 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah" 270 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah"); 271 std::string username; 272 std::string token; 273 ASSERT_TRUE(settings.GetLastUserAndServiceToken("service", &username, 274 &token)); 275 EXPECT_EQ( 276 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah" 277 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah" 278 "oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah", 279 token); 280 EXPECT_EQ("username", username); 281 } 282