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.
5 #include "chrome/browser/profiles/profile.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/json/json_reader.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/values.h"
15 #include "base/version.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/profiles/chrome_version_service.h"
19 #include "chrome/browser/profiles/profile_impl.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/profiles/startup_task_runner_service.h"
22 #include "chrome/browser/profiles/startup_task_runner_service_factory.h"
23 #include "chrome/common/chrome_constants.h"
24 #include "chrome/common/chrome_version_info.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/test/base/in_process_browser_test.h"
27 #include "content/public/test/test_utils.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 #if defined(OS_CHROMEOS)
32 #include "chrome/browser/chromeos/profiles/profile_helper.h"
33 #include "chromeos/chromeos_switches.h"
38 class MockProfileDelegate
: public Profile::Delegate
{
40 MOCK_METHOD1(OnPrefsLoaded
, void(Profile
*));
41 MOCK_METHOD3(OnProfileCreated
, void(Profile
*, bool, bool));
44 // Creates a prefs file in the given directory.
45 void CreatePrefsFileInDirectory(const base::FilePath
& directory_path
) {
46 base::FilePath
pref_path(directory_path
.Append(chrome::kPreferencesFilename
));
47 std::string
data("{}");
48 ASSERT_TRUE(base::WriteFile(pref_path
, data
.c_str(), data
.size()));
51 void CheckChromeVersion(Profile
*profile
, bool is_new
) {
52 std::string created_by_version
;
54 chrome::VersionInfo version_info
;
55 created_by_version
= version_info
.Version();
57 created_by_version
= "1.0.0.0";
59 std::string pref_version
=
60 ChromeVersionService::GetVersion(profile
->GetPrefs());
61 // Assert that created_by_version pref gets set to current version.
62 EXPECT_EQ(created_by_version
, pref_version
);
66 base::WaitableEvent
* is_blocked
,
67 base::WaitableEvent
* unblock
) {
72 void FlushTaskRunner(base::SequencedTaskRunner
* runner
) {
74 base::WaitableEvent
unblock(false, false);
76 runner
->PostTask(FROM_HERE
,
77 base::Bind(&base::WaitableEvent::Signal
, base::Unretained(&unblock
)));
83 // Give threads a chance to do their stuff before shutting down (i.e.
84 // deleting scoped temp dir etc).
85 // Should not be necessary anymore once Profile deletion is fixed
86 // (see crbug.com/88586).
87 content::RunAllPendingInMessageLoop();
88 content::RunAllPendingInMessageLoop(content::BrowserThread::DB
);
89 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
94 class ProfileBrowserTest
: public InProcessBrowserTest
{
96 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
97 #if defined(OS_CHROMEOS)
98 command_line
->AppendSwitch(
99 chromeos::switches::kIgnoreUserProfileMappingForTests
);
103 scoped_ptr
<Profile
> CreateProfile(
104 const base::FilePath
& path
,
105 Profile::Delegate
* delegate
,
106 Profile::CreateMode create_mode
) {
107 scoped_ptr
<Profile
> profile(Profile::CreateProfile(
108 path
, delegate
, create_mode
));
109 EXPECT_TRUE(profile
.get());
111 // Store the Profile's IO task runner so we can wind it down.
112 profile_io_task_runner_
= profile
->GetIOTaskRunner();
114 return profile
.Pass();
117 void FlushIoTaskRunnerAndSpinThreads() {
118 FlushTaskRunner(profile_io_task_runner_
.get());
122 scoped_refptr
<base::SequencedTaskRunner
> profile_io_task_runner_
;
125 // Test OnProfileCreate is called with is_new_profile set to true when
126 // creating a new profile synchronously.
127 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
, CreateNewProfileSynchronous
) {
128 base::ScopedTempDir temp_dir
;
129 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
131 MockProfileDelegate delegate
;
132 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, true));
135 scoped_ptr
<Profile
> profile(CreateProfile(
136 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_SYNCHRONOUS
));
137 CheckChromeVersion(profile
.get(), true);
140 FlushIoTaskRunnerAndSpinThreads();
143 // Test OnProfileCreate is called with is_new_profile set to false when
144 // creating a profile synchronously with an existing prefs file.
145 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
, CreateOldProfileSynchronous
) {
146 base::ScopedTempDir temp_dir
;
147 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
148 CreatePrefsFileInDirectory(temp_dir
.path());
150 MockProfileDelegate delegate
;
151 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, false));
154 scoped_ptr
<Profile
> profile(CreateProfile(
155 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_SYNCHRONOUS
));
156 CheckChromeVersion(profile
.get(), false);
159 FlushIoTaskRunnerAndSpinThreads();
162 // Flaky: http://crbug.com/393177
163 // Test OnProfileCreate is called with is_new_profile set to true when
164 // creating a new profile asynchronously.
165 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
,
166 DISABLED_CreateNewProfileAsynchronous
) {
167 base::ScopedTempDir temp_dir
;
168 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
170 MockProfileDelegate delegate
;
171 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, true));
174 content::WindowedNotificationObserver
observer(
175 chrome::NOTIFICATION_PROFILE_CREATED
,
176 content::NotificationService::AllSources());
178 scoped_ptr
<Profile
> profile(CreateProfile(
179 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_ASYNCHRONOUS
));
181 // Wait for the profile to be created.
183 CheckChromeVersion(profile
.get(), true);
186 FlushIoTaskRunnerAndSpinThreads();
190 // Flaky: http://crbug.com/393177
191 // Test OnProfileCreate is called with is_new_profile set to false when
192 // creating a profile asynchronously with an existing prefs file.
193 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
,
194 DISABLED_CreateOldProfileAsynchronous
) {
195 base::ScopedTempDir temp_dir
;
196 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
197 CreatePrefsFileInDirectory(temp_dir
.path());
199 MockProfileDelegate delegate
;
200 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, false));
203 content::WindowedNotificationObserver
observer(
204 chrome::NOTIFICATION_PROFILE_CREATED
,
205 content::NotificationService::AllSources());
207 scoped_ptr
<Profile
> profile(CreateProfile(
208 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_ASYNCHRONOUS
));
210 // Wait for the profile to be created.
212 CheckChromeVersion(profile
.get(), false);
215 FlushIoTaskRunnerAndSpinThreads();
218 // Flaky: http://crbug.com/393177
219 // Test that a README file is created for profiles that didn't have it.
220 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
, DISABLED_ProfileReadmeCreated
) {
221 base::ScopedTempDir temp_dir
;
222 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
224 MockProfileDelegate delegate
;
225 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, true));
227 // No delay before README creation.
228 ProfileImpl::create_readme_delay_ms
= 0;
231 content::WindowedNotificationObserver
observer(
232 chrome::NOTIFICATION_PROFILE_CREATED
,
233 content::NotificationService::AllSources());
235 scoped_ptr
<Profile
> profile(CreateProfile(
236 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_ASYNCHRONOUS
));
238 // Wait for the profile to be created.
241 // Wait for file thread to create the README.
242 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
244 // Verify that README exists.
245 EXPECT_TRUE(base::PathExists(
246 temp_dir
.path().Append(chrome::kReadmeFilename
)));
249 FlushIoTaskRunnerAndSpinThreads();
252 // Test that Profile can be deleted before README file is created.
253 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
, ProfileDeletedBeforeReadmeCreated
) {
254 base::ScopedTempDir temp_dir
;
255 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
257 MockProfileDelegate delegate
;
258 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, true));
260 // No delay before README creation.
261 ProfileImpl::create_readme_delay_ms
= 0;
263 base::WaitableEvent
is_blocked(false, false);
264 base::WaitableEvent
* unblock
= new base::WaitableEvent(false, false);
266 // Block file thread.
267 content::BrowserThread::PostTask(
268 content::BrowserThread::FILE, FROM_HERE
,
269 base::Bind(&BlockThread
, &is_blocked
, base::Owned(unblock
)));
270 // Wait for file thread to actually be blocked.
273 scoped_ptr
<Profile
> profile(CreateProfile(
274 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_SYNCHRONOUS
));
276 // Delete the Profile instance before we give the file thread a chance to
277 // create the README.
280 // Now unblock the file thread again and run pending tasks (this includes the
281 // task for README creation).
284 FlushIoTaskRunnerAndSpinThreads();
287 // Test that repeated setting of exit type is handled correctly.
288 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
, ExitType
) {
289 base::ScopedTempDir temp_dir
;
290 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
292 MockProfileDelegate delegate
;
293 EXPECT_CALL(delegate
, OnProfileCreated(testing::NotNull(), true, true));
295 scoped_ptr
<Profile
> profile(CreateProfile(
296 temp_dir
.path(), &delegate
, Profile::CREATE_MODE_SYNCHRONOUS
));
298 PrefService
* prefs
= profile
->GetPrefs();
299 // The initial state is crashed; store for later reference.
300 std::string
crash_value(prefs
->GetString(prefs::kSessionExitType
));
302 // The first call to a type other than crashed should change the value.
303 profile
->SetExitType(Profile::EXIT_SESSION_ENDED
);
304 std::string
first_call_value(prefs
->GetString(prefs::kSessionExitType
));
305 EXPECT_NE(crash_value
, first_call_value
);
307 // Subsequent calls to a non-crash value should be ignored.
308 profile
->SetExitType(Profile::EXIT_NORMAL
);
309 std::string
second_call_value(prefs
->GetString(prefs::kSessionExitType
));
310 EXPECT_EQ(first_call_value
, second_call_value
);
312 // Setting back to a crashed value should work.
313 profile
->SetExitType(Profile::EXIT_CRASHED
);
314 std::string
final_value(prefs
->GetString(prefs::kSessionExitType
));
315 EXPECT_EQ(crash_value
, final_value
);
318 FlushIoTaskRunnerAndSpinThreads();
321 // The EndSession IO synchronization is only critical on Windows, but also
322 // happens under the USE_X11 define. See BrowserProcessImpl::EndSession.
323 #if defined(USE_X11) || defined(OS_WIN)
327 std::string
GetExitTypePreferenceFromDisk(Profile
* profile
) {
328 base::FilePath prefs_path
=
329 profile
->GetPath().Append(chrome::kPreferencesFilename
);
331 if (!base::ReadFileToString(prefs_path
, &prefs
))
332 return std::string();
334 scoped_ptr
<base::Value
> value(base::JSONReader::Read(prefs
));
336 return std::string();
338 base::DictionaryValue
* dict
= NULL
;
339 if (!value
->GetAsDictionary(&dict
) || !dict
)
340 return std::string();
342 std::string exit_type
;
343 if (!dict
->GetString("profile.exit_type", &exit_type
))
344 return std::string();
351 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest
,
352 WritesProfilesSynchronouslyOnEndSession
) {
353 base::ScopedTempDir temp_dir
;
354 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
356 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
357 ASSERT_TRUE(profile_manager
);
358 std::vector
<Profile
*> loaded_profiles
= profile_manager
->GetLoadedProfiles();
360 ASSERT_NE(loaded_profiles
.size(), 0UL);
361 Profile
* profile
= loaded_profiles
[0];
363 #if defined(OS_CHROMEOS)
364 for (const auto& loaded_profile
: loaded_profiles
) {
365 if (!chromeos::ProfileHelper::IsSigninProfile(loaded_profile
)) {
366 profile
= loaded_profile
;
372 // This retry loop reduces flakiness due to the fact that this ultimately
373 // tests whether or not a code path hits a timed wait.
374 bool succeeded
= false;
375 for (size_t retries
= 0; !succeeded
&& retries
< 3; ++retries
) {
376 // Flush the profile data to disk for all loaded profiles.
377 profile
->SetExitType(Profile::EXIT_CRASHED
);
378 profile
->GetPrefs()->CommitPendingWrite();
379 FlushTaskRunner(profile
->GetIOTaskRunner().get());
381 // Make sure that the prefs file was written with the expected key/value.
382 ASSERT_EQ(GetExitTypePreferenceFromDisk(profile
), "Crashed");
384 // The blocking wait in EndSession has a timeout.
385 base::Time start
= base::Time::Now();
387 // This must not return until the profile data has been written to disk.
388 // If this test flakes, then logoff on Windows has broken again.
389 g_browser_process
->EndSession();
391 base::Time end
= base::Time::Now();
393 // The EndSession timeout is 10 seconds. If we take more than half that,
394 // go around again, as we may have timed out on the wait.
395 // This helps against flakes, and also ensures that if the IO thread starts
396 // blocking systemically for that length of time (e.g. deadlocking or such),
397 // we'll get a consistent test failure.
398 if (end
- start
> base::TimeDelta::FromSeconds(5))
401 // Make sure that the prefs file was written with the expected key/value.
402 ASSERT_EQ(GetExitTypePreferenceFromDisk(profile
), "SessionEnded");
408 ASSERT_TRUE(succeeded
) << "profile->EndSession() timed out too often.";
411 #endif // defined(USE_X11) || defined(OS_WIN)