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