Home | History | Annotate | Download | only in profiles
      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 
     36 namespace {
     37 
     38 class 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.
     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()));
     49 }
     50 
     51 void 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 
     65 void BlockThread(
     66     base::WaitableEvent* is_blocked,
     67     base::WaitableEvent* unblock) {
     68   is_blocked->Signal();
     69   unblock->Wait();
     70 }
     71 
     72 void 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 
     82 void 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 
     94 class 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.
    127 IN_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.
    145 IN_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.
    165 IN_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.
    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());
    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.
    220 IN_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.
    253 IN_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.
    288 IN_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 
    325 namespace {
    326 
    327 std::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 
    351 IN_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 
    401 IN_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