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
18using std::numeric_limits;
19
20namespace {
21
22const FilePath::CharType kV10UserSettingsDB[] =
23    FILE_PATH_LITERAL("Version10Settings.sqlite3");
24const FilePath::CharType kV11UserSettingsDB[] =
25    FILE_PATH_LITERAL("Version11Settings.sqlite3");
26const FilePath::CharType kOldStyleSyncDataDB[] =
27    FILE_PATH_LITERAL("OldStyleSyncData.sqlite3");
28
29}  // namespace
30
31class 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@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|.
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
167TEST_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
206TEST_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
237TEST_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
248TEST_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
262TEST_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