1// Copyright 2013 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/ui/sync/profile_signin_confirmation_helper.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/bind_helpers.h"
10#include "base/callback.h"
11#include "base/command_line.h"
12#include "base/compiler_specific.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_loop.h"
15#include "base/prefs/pref_notifier_impl.h"
16#include "base/prefs/pref_service.h"
17#include "base/prefs/testing_pref_service.h"
18#include "base/run_loop.h"
19#include "base/strings/string16.h"
20#include "base/strings/string_util.h"
21#include "base/strings/utf_string_conversions.h"
22#include "chrome/browser/bookmarks/bookmark_model_factory.h"
23#include "chrome/browser/history/history_service.h"
24#include "chrome/browser/history/history_service_factory.h"
25#include "chrome/browser/prefs/browser_prefs.h"
26#include "chrome/test/base/testing_pref_service_syncable.h"
27#include "chrome/test/base/testing_profile.h"
28#include "components/bookmarks/browser/bookmark_model.h"
29#include "components/bookmarks/test/bookmark_test_helpers.h"
30#include "components/pref_registry/pref_registry_syncable.h"
31#include "content/public/test/test_browser_thread_bundle.h"
32#include "content/public/test/test_utils.h"
33#include "testing/gmock/include/gmock/gmock.h"
34#include "testing/gtest/include/gtest/gtest.h"
35
36#if defined(OS_CHROMEOS)
37#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
38#include "chrome/browser/chromeos/settings/cros_settings.h"
39#include "chrome/browser/chromeos/settings/device_settings_service.h"
40#endif
41
42#if defined(ENABLE_EXTENSIONS)
43#include "chrome/browser/extensions/extension_service.h"
44#include "chrome/browser/extensions/test_extension_system.h"
45#include "chrome/common/extensions/extension_constants.h"
46#include "extensions/browser/extension_prefs.h"
47#include "extensions/common/constants.h"
48#include "extensions/common/extension.h"
49#include "extensions/common/manifest_constants.h"
50#include "extensions/common/permissions/permission_set.h"
51#endif
52
53namespace {
54
55template<typename T>
56void GetValueAndQuit(T* result, const base::Closure& quit, T actual) {
57  *result = actual;
58  quit.Run();
59}
60
61template<typename T>
62T GetCallbackResult(
63    const base::Callback<void(const base::Callback<void(T)>&)>& callback) {
64  T result = false;
65  base::RunLoop loop;
66  callback.Run(base::Bind(&GetValueAndQuit<T>, &result, loop.QuitClosure()));
67  loop.Run();
68  return result;
69}
70
71// A pref store that can have its read_error property changed for testing.
72class TestingPrefStoreWithCustomReadError : public TestingPrefStore {
73 public:
74  TestingPrefStoreWithCustomReadError()
75      : read_error_(PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
76    // By default the profile is "new" (NO_FILE means that the profile
77    // wasn't found on disk, so it was created).
78  }
79  virtual PrefReadError GetReadError() const OVERRIDE {
80    return read_error_;
81  }
82  virtual bool IsInitializationComplete() const OVERRIDE {
83    return true;
84  }
85  void set_read_error(PrefReadError read_error) {
86    read_error_ = read_error;
87  }
88 private:
89  virtual ~TestingPrefStoreWithCustomReadError() {}
90  PrefReadError read_error_;
91};
92
93#if defined(ENABLE_EXTENSIONS)
94#if defined(OS_WIN)
95const base::FilePath::CharType kExtensionFilePath[] =
96    FILE_PATH_LITERAL("c:\\foo");
97#elif defined(OS_POSIX)
98const base::FilePath::CharType kExtensionFilePath[] =
99    FILE_PATH_LITERAL("/oo");
100#endif
101
102static scoped_refptr<extensions::Extension> CreateExtension(
103    const std::string& name,
104    const std::string& id) {
105  base::DictionaryValue manifest;
106  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
107  manifest.SetString(extensions::manifest_keys::kName, name);
108  std::string error;
109  scoped_refptr<extensions::Extension> extension =
110    extensions::Extension::Create(
111        base::FilePath(kExtensionFilePath).AppendASCII(name),
112        extensions::Manifest::INTERNAL,
113        manifest,
114        extensions::Extension::NO_FLAGS,
115        id,
116        &error);
117  return extension;
118}
119#endif
120
121}  // namespace
122
123class ProfileSigninConfirmationHelperTest : public testing::Test {
124 public:
125  ProfileSigninConfirmationHelperTest()
126      : user_prefs_(NULL),
127        model_(NULL) {
128  }
129
130  virtual void SetUp() OVERRIDE {
131    // Create the profile.
132    TestingProfile::Builder builder;
133    user_prefs_ = new TestingPrefStoreWithCustomReadError;
134    TestingPrefServiceSyncable* pref_service = new TestingPrefServiceSyncable(
135        new TestingPrefStore(),
136        user_prefs_,
137        new TestingPrefStore(),
138        new user_prefs::PrefRegistrySyncable(),
139        new PrefNotifierImpl());
140    chrome::RegisterUserProfilePrefs(pref_service->registry());
141    builder.SetPrefService(make_scoped_ptr<PrefServiceSyncable>(pref_service));
142    profile_ = builder.Build();
143
144    // Initialize the services we check.
145    profile_->CreateBookmarkModel(true);
146    model_ = BookmarkModelFactory::GetForProfile(profile_.get());
147    test::WaitForBookmarkModelToLoad(model_);
148    ASSERT_TRUE(profile_->CreateHistoryService(true, false));
149#if defined(ENABLE_EXTENSIONS)
150    extensions::TestExtensionSystem* system =
151        static_cast<extensions::TestExtensionSystem*>(
152            extensions::ExtensionSystem::Get(profile_.get()));
153    CommandLine command_line(CommandLine::NO_PROGRAM);
154    system->CreateExtensionService(&command_line,
155                                   base::FilePath(kExtensionFilePath),
156                                   false);
157#endif
158  }
159
160  virtual void TearDown() OVERRIDE {
161    // TestExtensionSystem uses DeleteSoon, so we need to delete the profile
162    // and then run the message queue to clean up.
163    profile_.reset();
164    base::RunLoop().RunUntilIdle();
165  }
166
167 protected:
168  content::TestBrowserThreadBundle thread_bundle_;
169  scoped_ptr<TestingProfile> profile_;
170  TestingPrefStoreWithCustomReadError* user_prefs_;
171  BookmarkModel* model_;
172
173#if defined OS_CHROMEOS
174  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
175  chromeos::ScopedTestCrosSettings test_cros_settings_;
176  chromeos::ScopedTestUserManager test_user_manager_;
177#endif
178};
179
180// http://crbug.com/393149
181TEST_F(ProfileSigninConfirmationHelperTest, DISABLED_DoNotPromptForNewProfile) {
182  // Profile is new and there's no profile data.
183  EXPECT_FALSE(
184      GetCallbackResult(
185          base::Bind(
186              &ui::CheckShouldPromptForNewProfile,
187              profile_.get())));
188}
189
190TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Bookmarks) {
191  ASSERT_TRUE(model_);
192
193  // Profile is new but has bookmarks.
194  model_->AddURL(model_->bookmark_bar_node(), 0,
195                 base::string16(base::ASCIIToUTF16("foo")),
196                 GURL("http://foo.com"));
197  EXPECT_TRUE(
198      GetCallbackResult(
199          base::Bind(
200              &ui::CheckShouldPromptForNewProfile,
201              profile_.get())));
202}
203
204#if defined(ENABLE_EXTENSIONS)
205TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Extensions) {
206  ExtensionService* extensions =
207      extensions::ExtensionSystem::Get(profile_.get())->extension_service();
208  ASSERT_TRUE(extensions);
209
210  // Profile is new but has synced extensions.
211
212  // (The web store doesn't count.)
213  scoped_refptr<extensions::Extension> webstore =
214      CreateExtension("web store", extensions::kWebStoreAppId);
215  extensions::ExtensionPrefs::Get(profile_.get())->AddGrantedPermissions(
216      webstore->id(), make_scoped_refptr(new extensions::PermissionSet).get());
217  extensions->AddExtension(webstore.get());
218  EXPECT_FALSE(GetCallbackResult(
219      base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get())));
220
221  scoped_refptr<extensions::Extension> extension =
222      CreateExtension("foo", std::string());
223  extensions::ExtensionPrefs::Get(profile_.get())->AddGrantedPermissions(
224      extension->id(), make_scoped_refptr(new extensions::PermissionSet).get());
225  extensions->AddExtension(extension.get());
226  EXPECT_TRUE(GetCallbackResult(
227      base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get())));
228}
229#endif
230
231// http://crbug.com/393149
232TEST_F(ProfileSigninConfirmationHelperTest,
233       DISABLED_PromptForNewProfile_History) {
234  HistoryService* history = HistoryServiceFactory::GetForProfile(
235      profile_.get(),
236      Profile::EXPLICIT_ACCESS);
237  ASSERT_TRUE(history);
238
239  // Profile is new but has more than $(kHistoryEntriesBeforeNewProfilePrompt)
240  // history items.
241  char buf[18];
242  for (int i = 0; i < 10; i++) {
243    base::snprintf(buf, arraysize(buf), "http://foo.com/%d", i);
244    history->AddPage(
245        GURL(std::string(buf)), base::Time::Now(), NULL, 1,
246        GURL(), history::RedirectList(), ui::PAGE_TRANSITION_LINK,
247        history::SOURCE_BROWSED, false);
248  }
249  EXPECT_TRUE(
250      GetCallbackResult(
251          base::Bind(
252              &ui::CheckShouldPromptForNewProfile,
253              profile_.get())));
254}
255
256// http://crbug.com/393149
257TEST_F(ProfileSigninConfirmationHelperTest,
258       DISABLED_PromptForNewProfile_TypedURLs) {
259  HistoryService* history = HistoryServiceFactory::GetForProfile(
260      profile_.get(),
261      Profile::EXPLICIT_ACCESS);
262  ASSERT_TRUE(history);
263
264  // Profile is new but has a typed URL.
265  history->AddPage(
266      GURL("http://example.com"), base::Time::Now(), NULL, 1,
267      GURL(), history::RedirectList(), ui::PAGE_TRANSITION_TYPED,
268      history::SOURCE_BROWSED, false);
269  EXPECT_TRUE(
270      GetCallbackResult(
271          base::Bind(
272              &ui::CheckShouldPromptForNewProfile,
273              profile_.get())));
274}
275
276TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Restarted) {
277  // Browser has been shut down since profile was created.
278  user_prefs_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NONE);
279  EXPECT_TRUE(
280      GetCallbackResult(
281          base::Bind(
282              &ui::CheckShouldPromptForNewProfile,
283              profile_.get())));
284}
285