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/profiles/profile.h"
6
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/prefs/pref_service.h"
11#include "base/synchronization/waitable_event.h"
12#include "base/version.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/profiles/chrome_version_service.h"
15#include "chrome/browser/profiles/profile_impl.h"
16#include "chrome/browser/profiles/startup_task_runner_service.h"
17#include "chrome/browser/profiles/startup_task_runner_service_factory.h"
18#include "chrome/common/chrome_constants.h"
19#include "chrome/common/chrome_version_info.h"
20#include "chrome/common/pref_names.h"
21#include "chrome/test/base/in_process_browser_test.h"
22#include "chrome/test/base/ui_test_utils.h"
23#include "testing/gmock/include/gmock/gmock.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26#if defined(OS_CHROMEOS)
27#include "chromeos/chromeos_switches.h"
28#endif
29
30namespace {
31
32class MockProfileDelegate : public Profile::Delegate {
33 public:
34  MOCK_METHOD1(OnPrefsLoaded, void(Profile*));
35  MOCK_METHOD3(OnProfileCreated, void(Profile*, bool, bool));
36};
37
38// Creates a prefs file in the given directory.
39void CreatePrefsFileInDirectory(const base::FilePath& directory_path) {
40  base::FilePath pref_path(directory_path.Append(chrome::kPreferencesFilename));
41  std::string data("{}");
42  ASSERT_TRUE(base::WriteFile(pref_path, data.c_str(), data.size()));
43}
44
45scoped_ptr<Profile> CreateProfile(
46    const base::FilePath& path,
47    Profile::Delegate* delegate,
48    Profile::CreateMode create_mode) {
49  scoped_ptr<Profile> profile(Profile::CreateProfile(
50      path, delegate, create_mode));
51  EXPECT_TRUE(profile.get());
52  // This is necessary to avoid a memleak from BookmarkModel::Load.
53  // Unfortunately, this also results in warnings during debug runs.
54  StartupTaskRunnerServiceFactory::GetForProfile(profile.get())->
55      StartDeferredTaskRunners();
56  return profile.Pass();
57}
58
59void CheckChromeVersion(Profile *profile, bool is_new) {
60  std::string created_by_version;
61  if (is_new) {
62    chrome::VersionInfo version_info;
63    created_by_version = version_info.Version();
64  } else {
65    created_by_version = "1.0.0.0";
66  }
67  std::string pref_version =
68      ChromeVersionService::GetVersion(profile->GetPrefs());
69  // Assert that created_by_version pref gets set to current version.
70  EXPECT_EQ(created_by_version, pref_version);
71}
72
73void BlockThread(
74    base::WaitableEvent* is_blocked,
75    base::WaitableEvent* unblock) {
76  is_blocked->Signal();
77  unblock->Wait();
78}
79
80void SpinThreads() {
81  // Give threads a chance to do their stuff before shutting down (i.e.
82  // deleting scoped temp dir etc).
83  // Should not be necessary anymore once Profile deletion is fixed
84  // (see crbug.com/88586).
85  content::RunAllPendingInMessageLoop();
86  content::RunAllPendingInMessageLoop(content::BrowserThread::DB);
87  content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
88}
89
90}  // namespace
91
92class ProfileBrowserTest : public InProcessBrowserTest {
93 protected:
94  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
95#if defined(OS_CHROMEOS)
96    command_line->AppendSwitch(
97        chromeos::switches::kIgnoreUserProfileMappingForTests);
98#endif
99  }
100};
101
102// Test OnProfileCreate is called with is_new_profile set to true when
103// creating a new profile synchronously.
104//
105// Flaky (sometimes timeout): http://crbug.com/141141
106IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
107                       DISABLED_CreateNewProfileSynchronous) {
108  base::ScopedTempDir temp_dir;
109  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
110
111  MockProfileDelegate delegate;
112  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
113
114  {
115    scoped_ptr<Profile> profile(CreateProfile(
116        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
117    CheckChromeVersion(profile.get(), true);
118  }
119
120  SpinThreads();
121}
122
123// Test OnProfileCreate is called with is_new_profile set to false when
124// creating a profile synchronously with an existing prefs file.
125// Flaky: http://crbug.com/141517
126IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
127                       DISABLED_CreateOldProfileSynchronous) {
128  base::ScopedTempDir temp_dir;
129  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
130  CreatePrefsFileInDirectory(temp_dir.path());
131
132  MockProfileDelegate delegate;
133  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, false));
134
135  {
136    scoped_ptr<Profile> profile(CreateProfile(
137        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
138    CheckChromeVersion(profile.get(), false);
139  }
140
141  SpinThreads();
142}
143
144// Test OnProfileCreate is called with is_new_profile set to true when
145// creating a new profile asynchronously.
146// This test is flaky on Linux, Win and Mac.  See crbug.com/142787
147IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
148                       DISABLED_CreateNewProfileAsynchronous) {
149  base::ScopedTempDir temp_dir;
150  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
151
152  MockProfileDelegate delegate;
153  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
154
155  {
156    content::WindowedNotificationObserver observer(
157        chrome::NOTIFICATION_PROFILE_CREATED,
158        content::NotificationService::AllSources());
159
160    scoped_ptr<Profile> profile(CreateProfile(
161        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
162
163    // Wait for the profile to be created.
164    observer.Wait();
165    CheckChromeVersion(profile.get(), true);
166  }
167
168  SpinThreads();
169}
170
171// Test OnProfileCreate is called with is_new_profile set to false when
172// creating a profile asynchronously with an existing prefs file.
173// Flaky: http://crbug.com/141517
174IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
175                       DISABLED_CreateOldProfileAsynchronous) {
176  base::ScopedTempDir temp_dir;
177  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
178  CreatePrefsFileInDirectory(temp_dir.path());
179
180  MockProfileDelegate delegate;
181  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, false));
182
183  {
184    content::WindowedNotificationObserver observer(
185        chrome::NOTIFICATION_PROFILE_CREATED,
186        content::NotificationService::AllSources());
187
188    scoped_ptr<Profile> profile(CreateProfile(
189        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
190
191    // Wait for the profile to be created.
192    observer.Wait();
193    CheckChromeVersion(profile.get(), false);
194  }
195
196  SpinThreads();
197}
198
199// Test that a README file is created for profiles that didn't have it.
200// Flaky: http://crbug.com/140882
201IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DISABLED_ProfileReadmeCreated) {
202  base::ScopedTempDir temp_dir;
203  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
204
205  MockProfileDelegate delegate;
206  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
207
208  // No delay before README creation.
209  ProfileImpl::create_readme_delay_ms = 0;
210
211  {
212    content::WindowedNotificationObserver observer(
213        chrome::NOTIFICATION_PROFILE_CREATED,
214        content::NotificationService::AllSources());
215
216    scoped_ptr<Profile> profile(CreateProfile(
217        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
218
219    // Wait for the profile to be created.
220    observer.Wait();
221
222    // Wait for file thread to create the README.
223    content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
224
225    // Verify that README exists.
226    EXPECT_TRUE(base::PathExists(
227        temp_dir.path().Append(chrome::kReadmeFilename)));
228  }
229
230  SpinThreads();
231}
232
233// Test that Profile can be deleted before README file is created.
234IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ProfileDeletedBeforeReadmeCreated) {
235  base::ScopedTempDir temp_dir;
236  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
237
238  MockProfileDelegate delegate;
239  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
240
241  // No delay before README creation.
242  ProfileImpl::create_readme_delay_ms = 0;
243
244  base::WaitableEvent is_blocked(false, false);
245  base::WaitableEvent* unblock = new base::WaitableEvent(false, false);
246
247  // Block file thread.
248  content::BrowserThread::PostTask(
249      content::BrowserThread::FILE, FROM_HERE,
250      base::Bind(&BlockThread, &is_blocked, base::Owned(unblock)));
251  // Wait for file thread to actually be blocked.
252  is_blocked.Wait();
253
254  scoped_ptr<Profile> profile(CreateProfile(
255      temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
256
257  // Delete the Profile instance before we give the file thread a chance to
258  // create the README.
259  profile.reset();
260
261  // Now unblock the file thread again and run pending tasks (this includes the
262  // task for README creation).
263  unblock->Signal();
264
265  SpinThreads();
266}
267
268// Test that repeated setting of exit type is handled correctly.
269#if defined(OS_WIN)
270// Flaky on Windows: http://crbug.com/163713
271#define MAYBE_ExitType DISABLED_ExitType
272#else
273#define MAYBE_ExitType ExitType
274#endif
275IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_ExitType) {
276  base::ScopedTempDir temp_dir;
277  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
278
279  MockProfileDelegate delegate;
280  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
281  {
282    scoped_ptr<Profile> profile(CreateProfile(
283        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
284
285    PrefService* prefs = profile->GetPrefs();
286    // The initial state is crashed; store for later reference.
287    std::string crash_value(prefs->GetString(prefs::kSessionExitType));
288
289    // The first call to a type other than crashed should change the value.
290    profile->SetExitType(Profile::EXIT_SESSION_ENDED);
291    std::string first_call_value(prefs->GetString(prefs::kSessionExitType));
292    EXPECT_NE(crash_value, first_call_value);
293
294    // Subsequent calls to a non-crash value should be ignored.
295    profile->SetExitType(Profile::EXIT_NORMAL);
296    std::string second_call_value(prefs->GetString(prefs::kSessionExitType));
297    EXPECT_EQ(first_call_value, second_call_value);
298
299    // Setting back to a crashed value should work.
300    profile->SetExitType(Profile::EXIT_CRASHED);
301    std::string final_value(prefs->GetString(prefs::kSessionExitType));
302    EXPECT_EQ(crash_value, final_value);
303  }
304
305  SpinThreads();
306}
307