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:
UserSettingsTest()33 UserSettingsTest() : sync_data_("Some sync data") {}
34
SetUp()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|.
SetUpVersion10Databases(const FilePath & 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@foo.com");
94 s.BindString(1, "foo@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|.
SetUpVersion11Database(const FilePath & 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
sync_data() const147 const std::string& sync_data() const { return sync_data_; }
v10_user_setting_db_path() const148 const FilePath& v10_user_setting_db_path() const {
149 return v10_user_setting_db_path_;
150 }
v11_user_setting_db_path() const151 const FilePath& v11_user_setting_db_path() const {
152 return v11_user_setting_db_path_;
153 }
old_style_sync_data_path() const154 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
TEST_F(UserSettingsTest,MigrateFromV10ToV11)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
TEST_F(UserSettingsTest,MigrateFromV11ToV12)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
TEST_F(UserSettingsTest,APEncode)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
TEST_F(UserSettingsTest,PersistEmptyToken)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
TEST_F(UserSettingsTest,PersistNonEmptyToken)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