// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h" #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/prefs/pref_notifier_impl.h" #include "base/prefs/pref_service.h" #include "base/prefs/testing_pref_service.h" #include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/bookmarks/bookmark_test_helpers.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/history/history_service.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/permissions/permission_set.h" #include "chrome/test/base/testing_pref_service_syncable.h" #include "chrome/test/base/testing_profile.h" #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "extensions/common/manifest_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #endif namespace { template void GetValueAndQuit(T* result, const base::Closure& quit, T actual) { *result = actual; quit.Run(); } template T GetCallbackResult( const base::Callback&)>& callback) { T result = false; base::RunLoop loop; callback.Run(base::Bind(&GetValueAndQuit, &result, loop.QuitClosure())); loop.Run(); return result; } // A pref store that can have its read_error property changed for testing. class TestingPrefStoreWithCustomReadError : public TestingPrefStore { public: TestingPrefStoreWithCustomReadError() : read_error_(PersistentPrefStore::PREF_READ_ERROR_NO_FILE) { // By default the profile is "new" (NO_FILE means that the profile // wasn't found on disk, so it was created). } virtual PrefReadError GetReadError() const OVERRIDE { return read_error_; } virtual bool IsInitializationComplete() const OVERRIDE { return true; } void set_read_error(PrefReadError read_error) { read_error_ = read_error; } private: virtual ~TestingPrefStoreWithCustomReadError() {} PrefReadError read_error_; }; #if defined(OS_WIN) const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("c:\\foo"); #elif defined(OS_POSIX) const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("/oo"); #endif static scoped_refptr CreateExtension( const std::string& name, const std::string& id) { DictionaryValue manifest; manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0"); manifest.SetString(extensions::manifest_keys::kName, name); std::string error; scoped_refptr extension = extensions::Extension::Create( base::FilePath(kExtensionFilePath).AppendASCII(name), extensions::Manifest::INTERNAL, manifest, extensions::Extension::NO_FLAGS, id, &error); return extension; } } // namespace class ProfileSigninConfirmationHelperTest : public testing::Test { public: ProfileSigninConfirmationHelperTest() : user_prefs_(NULL), model_(NULL) { } virtual void SetUp() OVERRIDE { // Create the profile. TestingProfile::Builder builder; user_prefs_ = new TestingPrefStoreWithCustomReadError; TestingPrefServiceSyncable* pref_service = new TestingPrefServiceSyncable( new TestingPrefStore(), user_prefs_, new TestingPrefStore(), new user_prefs::PrefRegistrySyncable(), new PrefNotifierImpl()); chrome::RegisterUserProfilePrefs(pref_service->registry()); builder.SetPrefService(make_scoped_ptr(pref_service)); profile_ = builder.Build(); // Initialize the services we check. profile_->CreateBookmarkModel(true); model_ = BookmarkModelFactory::GetForProfile(profile_.get()); test::WaitForBookmarkModelToLoad(model_); ASSERT_TRUE(profile_->CreateHistoryService(true, false)); extensions::TestExtensionSystem* system = static_cast( extensions::ExtensionSystem::Get(profile_.get())); CommandLine command_line(CommandLine::NO_PROGRAM); system->CreateExtensionService(&command_line, base::FilePath(kExtensionFilePath), false); } virtual void TearDown() OVERRIDE { // TestExtensionSystem uses DeleteSoon, so we need to delete the profile // and then run the message queue to clean up. profile_.reset(); base::RunLoop().RunUntilIdle(); } protected: content::TestBrowserThreadBundle thread_bundle_; scoped_ptr profile_; TestingPrefStoreWithCustomReadError* user_prefs_; BookmarkModel* model_; #if defined OS_CHROMEOS chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; chromeos::ScopedTestCrosSettings test_cros_settings_; chromeos::ScopedTestUserManager test_user_manager_; #endif }; TEST_F(ProfileSigninConfirmationHelperTest, DoNotPromptForNewProfile) { // Profile is new and there's no profile data. EXPECT_FALSE( GetCallbackResult( base::Bind( &ui::CheckShouldPromptForNewProfile, profile_.get()))); } TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Bookmarks) { ASSERT_TRUE(model_); // Profile is new but has bookmarks. model_->AddURL(model_->bookmark_bar_node(), 0, string16(ASCIIToUTF16("foo")), GURL("http://foo.com")); EXPECT_TRUE( GetCallbackResult( base::Bind( &ui::CheckShouldPromptForNewProfile, profile_.get()))); } TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Extensions) { ExtensionService* extensions = extensions::ExtensionSystem::Get(profile_.get())->extension_service(); ASSERT_TRUE(extensions); // Profile is new but has synced extensions. // (The web store doesn't count.) scoped_refptr webstore = CreateExtension("web store", extension_misc::kWebStoreAppId); extensions->extension_prefs()->AddGrantedPermissions( webstore->id(), make_scoped_refptr(new extensions::PermissionSet).get()); extensions->AddExtension(webstore.get()); EXPECT_FALSE(GetCallbackResult( base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get()))); scoped_refptr extension = CreateExtension("foo", std::string()); extensions->extension_prefs()->AddGrantedPermissions( extension->id(), make_scoped_refptr(new extensions::PermissionSet).get()); extensions->AddExtension(extension.get()); EXPECT_TRUE(GetCallbackResult( base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get()))); } TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_History) { HistoryService* history = HistoryServiceFactory::GetForProfile( profile_.get(), Profile::EXPLICIT_ACCESS); ASSERT_TRUE(history); // Profile is new but has more than $(kHistoryEntriesBeforeNewProfilePrompt) // history items. char buf[18]; for (int i = 0; i < 10; i++) { base::snprintf(buf, arraysize(buf), "http://foo.com/%d", i); history->AddPage( GURL(std::string(buf)), base::Time::Now(), NULL, 1, GURL(), history::RedirectList(), content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED, false); } EXPECT_TRUE( GetCallbackResult( base::Bind( &ui::CheckShouldPromptForNewProfile, profile_.get()))); } TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_TypedURLs) { HistoryService* history = HistoryServiceFactory::GetForProfile( profile_.get(), Profile::EXPLICIT_ACCESS); ASSERT_TRUE(history); // Profile is new but has a typed URL. history->AddPage( GURL("http://example.com"), base::Time::Now(), NULL, 1, GURL(), history::RedirectList(), content::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false); EXPECT_TRUE( GetCallbackResult( base::Bind( &ui::CheckShouldPromptForNewProfile, profile_.get()))); } TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Restarted) { // Browser has been shut down since profile was created. user_prefs_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NONE); EXPECT_TRUE( GetCallbackResult( base::Bind( &ui::CheckShouldPromptForNewProfile, profile_.get()))); }