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/files/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/json/json_reader.h"
11#include "base/metrics/field_trial.h"
12#include "base/prefs/pref_service.h"
13#include "base/sequenced_task_runner.h"
14#include "base/synchronization/waitable_event.h"
15#include "base/values.h"
16#include "base/version.h"
17#include "chrome/browser/browser_process.h"
18#include "chrome/browser/chrome_notification_types.h"
19#include "chrome/browser/profiles/chrome_version_service.h"
20#include "chrome/browser/profiles/profile_impl.h"
21#include "chrome/browser/profiles/profile_manager.h"
22#include "chrome/browser/profiles/startup_task_runner_service.h"
23#include "chrome/browser/profiles/startup_task_runner_service_factory.h"
24#include "chrome/common/chrome_constants.h"
25#include "chrome/common/chrome_version_info.h"
26#include "chrome/common/pref_names.h"
27#include "chrome/test/base/in_process_browser_test.h"
28#include "chrome/test/base/ui_test_utils.h"
29#include "testing/gmock/include/gmock/gmock.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32#if defined(OS_CHROMEOS)
33#include "chromeos/chromeos_switches.h"
34#endif
35
36namespace {
37
38class MockProfileDelegate : public Profile::Delegate {
39 public:
40  MOCK_METHOD1(OnPrefsLoaded, void(Profile*));
41  MOCK_METHOD3(OnProfileCreated, void(Profile*, bool, bool));
42};
43
44// Creates a prefs file in the given directory.
45void 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()));
49}
50
51void CheckChromeVersion(Profile *profile, bool is_new) {
52  std::string created_by_version;
53  if (is_new) {
54    chrome::VersionInfo version_info;
55    created_by_version = version_info.Version();
56  } else {
57    created_by_version = "1.0.0.0";
58  }
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);
63}
64
65void BlockThread(
66    base::WaitableEvent* is_blocked,
67    base::WaitableEvent* unblock) {
68  is_blocked->Signal();
69  unblock->Wait();
70}
71
72void FlushTaskRunner(base::SequencedTaskRunner* runner) {
73  ASSERT_TRUE(runner);
74  base::WaitableEvent unblock(false, false);
75
76  runner->PostTask(FROM_HERE,
77      base::Bind(&base::WaitableEvent::Signal, base::Unretained(&unblock)));
78
79  unblock.Wait();
80}
81
82void SpinThreads() {
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);
90}
91
92}  // namespace
93
94class ProfileBrowserTest : public InProcessBrowserTest {
95 protected:
96  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
97#if defined(OS_CHROMEOS)
98    command_line->AppendSwitch(
99        chromeos::switches::kIgnoreUserProfileMappingForTests);
100#endif
101  }
102
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());
110
111    // Store the Profile's IO task runner so we can wind it down.
112    profile_io_task_runner_ = profile->GetIOTaskRunner();
113
114    return profile.Pass();
115  }
116
117  void FlushIoTaskRunnerAndSpinThreads() {
118    FlushTaskRunner(profile_io_task_runner_.get());
119    SpinThreads();
120  }
121
122  scoped_refptr<base::SequencedTaskRunner> profile_io_task_runner_;
123};
124
125// Test OnProfileCreate is called with is_new_profile set to true when
126// creating a new profile synchronously.
127IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateNewProfileSynchronous) {
128  base::ScopedTempDir temp_dir;
129  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
130
131  MockProfileDelegate delegate;
132  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
133
134  {
135    scoped_ptr<Profile> profile(CreateProfile(
136        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
137    CheckChromeVersion(profile.get(), true);
138  }
139
140  FlushIoTaskRunnerAndSpinThreads();
141}
142
143// Test OnProfileCreate is called with is_new_profile set to false when
144// creating a profile synchronously with an existing prefs file.
145IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateOldProfileSynchronous) {
146  base::ScopedTempDir temp_dir;
147  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
148  CreatePrefsFileInDirectory(temp_dir.path());
149
150  MockProfileDelegate delegate;
151  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, false));
152
153  {
154    scoped_ptr<Profile> profile(CreateProfile(
155        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
156    CheckChromeVersion(profile.get(), false);
157  }
158
159  FlushIoTaskRunnerAndSpinThreads();
160}
161
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.
165IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
166                       DISABLED_CreateNewProfileAsynchronous) {
167  base::ScopedTempDir temp_dir;
168  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
169
170  MockProfileDelegate delegate;
171  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
172
173  {
174    content::WindowedNotificationObserver observer(
175        chrome::NOTIFICATION_PROFILE_CREATED,
176        content::NotificationService::AllSources());
177
178    scoped_ptr<Profile> profile(CreateProfile(
179        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
180
181    // Wait for the profile to be created.
182    observer.Wait();
183    CheckChromeVersion(profile.get(), true);
184  }
185
186  FlushIoTaskRunnerAndSpinThreads();
187}
188
189
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.
193IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
194                       DISABLED_CreateOldProfileAsynchronous) {
195  base::ScopedTempDir temp_dir;
196  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
197  CreatePrefsFileInDirectory(temp_dir.path());
198
199  MockProfileDelegate delegate;
200  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, false));
201
202  {
203    content::WindowedNotificationObserver observer(
204        chrome::NOTIFICATION_PROFILE_CREATED,
205        content::NotificationService::AllSources());
206
207    scoped_ptr<Profile> profile(CreateProfile(
208        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
209
210    // Wait for the profile to be created.
211    observer.Wait();
212    CheckChromeVersion(profile.get(), false);
213  }
214
215  FlushIoTaskRunnerAndSpinThreads();
216}
217
218// Flaky: http://crbug.com/393177
219// Test that a README file is created for profiles that didn't have it.
220IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DISABLED_ProfileReadmeCreated) {
221  base::ScopedTempDir temp_dir;
222  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
223
224  MockProfileDelegate delegate;
225  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
226
227  // No delay before README creation.
228  ProfileImpl::create_readme_delay_ms = 0;
229
230  {
231    content::WindowedNotificationObserver observer(
232        chrome::NOTIFICATION_PROFILE_CREATED,
233        content::NotificationService::AllSources());
234
235    scoped_ptr<Profile> profile(CreateProfile(
236        temp_dir.path(), &delegate, Profile::CREATE_MODE_ASYNCHRONOUS));
237
238    // Wait for the profile to be created.
239    observer.Wait();
240
241    // Wait for file thread to create the README.
242    content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
243
244    // Verify that README exists.
245    EXPECT_TRUE(base::PathExists(
246        temp_dir.path().Append(chrome::kReadmeFilename)));
247  }
248
249  FlushIoTaskRunnerAndSpinThreads();
250}
251
252// Test that Profile can be deleted before README file is created.
253IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ProfileDeletedBeforeReadmeCreated) {
254  base::ScopedTempDir temp_dir;
255  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
256
257  MockProfileDelegate delegate;
258  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
259
260  // No delay before README creation.
261  ProfileImpl::create_readme_delay_ms = 0;
262
263  base::WaitableEvent is_blocked(false, false);
264  base::WaitableEvent* unblock = new base::WaitableEvent(false, false);
265
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.
271  is_blocked.Wait();
272
273  scoped_ptr<Profile> profile(CreateProfile(
274      temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
275
276  // Delete the Profile instance before we give the file thread a chance to
277  // create the README.
278  profile.reset();
279
280  // Now unblock the file thread again and run pending tasks (this includes the
281  // task for README creation).
282  unblock->Signal();
283
284  FlushIoTaskRunnerAndSpinThreads();
285}
286
287// Test that repeated setting of exit type is handled correctly.
288IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ExitType) {
289  base::ScopedTempDir temp_dir;
290  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
291
292  MockProfileDelegate delegate;
293  EXPECT_CALL(delegate, OnProfileCreated(testing::NotNull(), true, true));
294  {
295    scoped_ptr<Profile> profile(CreateProfile(
296        temp_dir.path(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
297
298    PrefService* prefs = profile->GetPrefs();
299    // The initial state is crashed; store for later reference.
300    std::string crash_value(prefs->GetString(prefs::kSessionExitType));
301
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);
306
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);
311
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);
316  }
317
318  FlushIoTaskRunnerAndSpinThreads();
319}
320
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)
324
325namespace {
326
327std::string GetExitTypePreferenceFromDisk(Profile* profile) {
328  base::FilePath prefs_path =
329      profile->GetPath().Append(chrome::kPreferencesFilename);
330  std::string prefs;
331  if (!base::ReadFileToString(prefs_path, &prefs))
332    return std::string();
333
334  scoped_ptr<base::Value> value(base::JSONReader::Read(prefs));
335  if (!value)
336    return std::string();
337
338  base::DictionaryValue* dict = NULL;
339  if (!value->GetAsDictionary(&dict) || !dict)
340    return std::string();
341
342  std::string exit_type;
343  if (!dict->GetString("profile.exit_type", &exit_type))
344    return std::string();
345
346  return exit_type;
347}
348
349}  // namespace
350
351IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
352                       WritesProfilesSynchronouslyOnEndSession) {
353  base::ScopedTempDir temp_dir;
354  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
355
356  ProfileManager* profile_manager = g_browser_process->profile_manager();
357  ASSERT_TRUE(profile_manager);
358  std::vector<Profile*> loaded_profiles = profile_manager->GetLoadedProfiles();
359
360  ASSERT_NE(loaded_profiles.size(), 0UL);
361  Profile* profile = loaded_profiles[0];
362
363  // This retry loop reduces flakiness due to the fact that this ultimately
364  // tests whether or not a code path hits a timed wait.
365  bool succeeded = false;
366  for (size_t retries = 0; !succeeded && retries < 3; ++retries) {
367    // Flush the profile data to disk for all loaded profiles.
368    profile->SetExitType(Profile::EXIT_CRASHED);
369    FlushTaskRunner(profile->GetIOTaskRunner().get());
370
371    // Make sure that the prefs file was written with the expected key/value.
372    ASSERT_EQ(GetExitTypePreferenceFromDisk(profile), "Crashed");
373
374    // The blocking wait in EndSession has a timeout.
375    base::Time start = base::Time::Now();
376
377    // This must not return until the profile data has been written to disk.
378    // If this test flakes, then logoff on Windows has broken again.
379    g_browser_process->EndSession();
380
381    base::Time end = base::Time::Now();
382
383    // The EndSession timeout is 10 seconds. If we take more than half that,
384    // go around again, as we may have timed out on the wait.
385    // This helps against flakes, and also ensures that if the IO thread starts
386    // blocking systemically for that length of time (e.g. deadlocking or such),
387    // we'll get a consistent test failure.
388    if (end - start > base::TimeDelta::FromSeconds(5))
389      continue;
390
391    // Make sure that the prefs file was written with the expected key/value.
392    ASSERT_EQ(GetExitTypePreferenceFromDisk(profile), "SessionEnded");
393
394    // Mark the success.
395    succeeded = true;
396  }
397
398  ASSERT_TRUE(succeeded) << "profile->EndSession() timed out too often.";
399}
400
401IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
402                       EndSessionBrokenSynchronizationExperiment) {
403  base::ScopedTempDir temp_dir;
404  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
405
406  // Select into the field trial group.
407  base::FieldTrialList::CreateFieldTrial("WindowsLogoffRace",
408                                         "BrokenSynchronization");
409
410  ProfileManager* profile_manager = g_browser_process->profile_manager();
411  ASSERT_TRUE(profile_manager);
412  std::vector<Profile*> loaded_profiles = profile_manager->GetLoadedProfiles();
413
414  ASSERT_NE(loaded_profiles.size(), 0UL);
415  Profile* profile = loaded_profiles[0];
416
417  bool mis_wrote = false;
418  // This retry loop reduces flakiness due to the fact that this ultimately
419  // tests whether or not a code path hits a timed wait.
420  for (size_t retries = 0; retries < 3; ++retries) {
421    // Flush the profile data to disk for all loaded profiles.
422    profile->SetExitType(Profile::EXIT_CRASHED);
423    FlushTaskRunner(profile->GetIOTaskRunner().get());
424
425    // Make sure that the prefs file was written with the expected key/value.
426    ASSERT_EQ(GetExitTypePreferenceFromDisk(profile), "Crashed");
427
428    base::WaitableEvent is_blocked(false, false);
429    base::WaitableEvent* unblock = new base::WaitableEvent(false, false);
430
431    // Block the profile's IO thread.
432    profile->GetIOTaskRunner()->PostTask(FROM_HERE,
433        base::Bind(&BlockThread, &is_blocked, base::Owned(unblock)));
434    // Wait for the IO thread to actually be blocked.
435    is_blocked.Wait();
436
437    // The blocking wait in EndSession has a timeout, so a non-write can only be
438    // concluded if it happens in less time than the timeout.
439    base::Time start = base::Time::Now();
440
441    // With the broken synchronization this is expected to return without
442    // blocking for the Profile's IO thread.
443    g_browser_process->EndSession();
444
445    base::Time end = base::Time::Now();
446
447    // The EndSession timeout is 10 seconds, we take a 5 second run-through as
448    // sufficient proof that we didn't hit the timed wait.
449    if (end - start < base::TimeDelta::FromSeconds(5)) {
450      // Make sure that the prefs file is unmodified with the expected
451      // key/value.
452      EXPECT_EQ(GetExitTypePreferenceFromDisk(profile), "Crashed");
453      mis_wrote = true;
454    }
455
456    // Release the IO thread thread.
457    unblock->Signal();
458  }
459
460  ASSERT_TRUE(mis_wrote);
461}
462
463#endif  // defined(USE_X11) || defined(OS_WIN)
464