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