profile_resetter_unittest.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1// Copyright (c) 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/profile_resetter/profile_resetter.h"
6
7#include "base/json/json_string_value_serializer.h"
8#include "base/prefs/pref_service.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/test/scoped_path_override.h"
11#include "chrome/browser/content_settings/host_content_settings_map.h"
12#include "chrome/browser/extensions/extension_service_unittest.h"
13#include "chrome/browser/extensions/tab_helper.h"
14#include "chrome/browser/notifications/desktop_notification_service.h"
15#include "chrome/browser/notifications/desktop_notification_service_factory.h"
16#include "chrome/browser/prefs/session_startup_pref.h"
17#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
18#include "chrome/browser/profile_resetter/profile_resetter_test_base.h"
19#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
20#include "chrome/browser/search_engines/template_url_service.h"
21#include "chrome/browser/search_engines/template_url_service_factory.h"
22#include "chrome/browser/themes/theme_service.h"
23#include "chrome/browser/themes/theme_service_factory.h"
24#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/test/base/browser_with_test_window_test.h"
27#include "components/google/core/browser/google_pref_names.h"
28#include "content/public/browser/web_contents.h"
29#include "content/public/test/test_browser_thread.h"
30#include "extensions/common/extension.h"
31#include "extensions/common/manifest_constants.h"
32#include "net/http/http_response_headers.h"
33#include "net/http/http_status_code.h"
34#include "net/url_request/test_url_fetcher_factory.h"
35#include "net/url_request/url_request_status.h"
36#include "url/gurl.h"
37
38#if defined(OS_WIN)
39#include "base/file_util.h"
40#include "base/path_service.h"
41#include "base/process/process_handle.h"
42#include "base/rand_util.h"
43#include "base/strings/string_number_conversions.h"
44#include "base/win/scoped_com_initializer.h"
45#include "base/win/shortcut.h"
46#endif
47
48
49namespace {
50
51const char kDistributionConfig[] = "{"
52    " \"homepage\" : \"http://www.foo.com\","
53    " \"homepage_is_newtabpage\" : false,"
54    " \"browser\" : {"
55    "   \"show_home_button\" : true"
56    "  },"
57    " \"session\" : {"
58    "   \"restore_on_startup\" : 4,"
59    "   \"startup_urls\" : [\"http://goo.gl\", \"http://foo.de\"]"
60    "  },"
61    " \"search_provider_overrides\" : ["
62    "    {"
63    "      \"name\" : \"first\","
64    "      \"keyword\" : \"firstkey\","
65    "      \"search_url\" : \"http://www.foo.com/s?q={searchTerms}\","
66    "      \"favicon_url\" : \"http://www.foo.com/favicon.ico\","
67    "      \"suggest_url\" : \"http://www.foo.com/s?q={searchTerms}\","
68    "      \"encoding\" : \"UTF-8\","
69    "      \"id\" : 1001"
70    "    }"
71    "  ],"
72    " \"extensions\" : {"
73    "   \"settings\" : {"
74    "     \"placeholder_for_id\": {"
75    "      }"
76    "    }"
77    "  }"
78    "}";
79
80const char kXmlConfig[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
81    "<response protocol=\"3.0\" server=\"prod\">"
82      "<app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\">"
83        "<data index=\"skipfirstrunui-importsearch-defaultbrowser\" "
84          "name=\"install\" status=\"ok\">"
85          "placeholder_for_data"
86        "</data>"
87      "</app>"
88    "</response>";
89
90using extensions::Extension;
91using extensions::Manifest;
92
93
94// ProfileResetterTest --------------------------------------------------------
95
96// ProfileResetterTest sets up the extension, WebData and TemplateURL services.
97class ProfileResetterTest : public ExtensionServiceTestBase,
98                            public ProfileResetterTestBase {
99 public:
100  ProfileResetterTest();
101  virtual ~ProfileResetterTest();
102
103 protected:
104  virtual void SetUp() OVERRIDE;
105
106  TestingProfile* profile() { return profile_.get(); }
107
108  static KeyedService* CreateTemplateURLService(
109      content::BrowserContext* context);
110
111 private:
112#if defined(OS_WIN)
113  base::ScopedPathOverride user_desktop_override_;
114  base::ScopedPathOverride app_dir_override_;
115  base::ScopedPathOverride start_menu_override_;
116  base::ScopedPathOverride taskbar_pins_override_;
117  base::win::ScopedCOMInitializer com_init_;
118#endif
119};
120
121ProfileResetterTest::ProfileResetterTest()
122#if defined(OS_WIN)
123    : user_desktop_override_(base::DIR_USER_DESKTOP),
124      app_dir_override_(base::DIR_APP_DATA),
125      start_menu_override_(base::DIR_START_MENU),
126      taskbar_pins_override_(base::DIR_TASKBAR_PINS)
127#endif
128{}
129
130ProfileResetterTest::~ProfileResetterTest() {
131}
132
133void ProfileResetterTest::SetUp() {
134  ExtensionServiceTestBase::SetUp();
135  InitializeEmptyExtensionService();
136
137  profile()->CreateWebDataService();
138  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
139      profile(),
140      &ProfileResetterTest::CreateTemplateURLService);
141  resetter_.reset(new ProfileResetter(profile()));
142}
143
144// static
145KeyedService* ProfileResetterTest::CreateTemplateURLService(
146    content::BrowserContext* context) {
147  return new TemplateURLService(static_cast<Profile*>(context));
148}
149
150
151// PinnedTabsResetTest --------------------------------------------------------
152
153class PinnedTabsResetTest : public BrowserWithTestWindowTest,
154                            public ProfileResetterTestBase {
155 protected:
156  virtual void SetUp() OVERRIDE;
157
158  content::WebContents* CreateWebContents();
159};
160
161void PinnedTabsResetTest::SetUp() {
162  BrowserWithTestWindowTest::SetUp();
163  resetter_.reset(new ProfileResetter(profile()));
164}
165
166content::WebContents* PinnedTabsResetTest::CreateWebContents() {
167  return content::WebContents::Create(
168      content::WebContents::CreateParams(profile()));
169}
170
171
172// ConfigParserTest -----------------------------------------------------------
173
174// URLFetcher delegate that simply records the upload data.
175struct URLFetcherRequestListener : net::URLFetcherDelegate {
176  URLFetcherRequestListener();
177  virtual ~URLFetcherRequestListener();
178
179  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
180
181  std::string upload_data;
182  net::URLFetcherDelegate* real_delegate;
183};
184
185URLFetcherRequestListener::URLFetcherRequestListener()
186    : real_delegate(NULL) {
187}
188
189URLFetcherRequestListener::~URLFetcherRequestListener() {
190}
191
192void URLFetcherRequestListener::OnURLFetchComplete(
193    const net::URLFetcher* source) {
194  const net::TestURLFetcher* test_fetcher =
195      static_cast<const net::TestURLFetcher*>(source);
196  upload_data = test_fetcher->upload_data();
197  DCHECK(real_delegate);
198  real_delegate->OnURLFetchComplete(source);
199}
200
201class ConfigParserTest : public testing::Test {
202 protected:
203  ConfigParserTest();
204  virtual ~ConfigParserTest();
205
206  scoped_ptr<BrandcodeConfigFetcher> WaitForRequest(const GURL& url);
207
208  net::FakeURLFetcherFactory& factory() { return factory_; }
209
210 private:
211  scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
212      const GURL& url,
213      net::URLFetcherDelegate* fetcher_delegate,
214      const std::string& response_data,
215      net::HttpStatusCode response_code,
216      net::URLRequestStatus::Status status);
217
218  MOCK_METHOD0(Callback, void(void));
219
220  base::MessageLoopForIO loop_;
221  content::TestBrowserThread ui_thread_;
222  content::TestBrowserThread io_thread_;
223  URLFetcherRequestListener request_listener_;
224  net::FakeURLFetcherFactory factory_;
225};
226
227ConfigParserTest::ConfigParserTest()
228    : ui_thread_(content::BrowserThread::UI, &loop_),
229      io_thread_(content::BrowserThread::IO, &loop_),
230      factory_(NULL, base::Bind(&ConfigParserTest::CreateFakeURLFetcher,
231                                base::Unretained(this))) {
232}
233
234ConfigParserTest::~ConfigParserTest() {}
235
236scoped_ptr<BrandcodeConfigFetcher> ConfigParserTest::WaitForRequest(
237    const GURL& url) {
238  EXPECT_CALL(*this, Callback());
239  scoped_ptr<BrandcodeConfigFetcher> fetcher(
240      new BrandcodeConfigFetcher(base::Bind(&ConfigParserTest::Callback,
241                                            base::Unretained(this)),
242                                 url,
243                                 "ABCD"));
244  base::MessageLoop::current()->RunUntilIdle();
245  EXPECT_FALSE(fetcher->IsActive());
246  // Look for the brand code in the request.
247  EXPECT_NE(std::string::npos, request_listener_.upload_data.find("ABCD"));
248  return fetcher.Pass();
249}
250
251scoped_ptr<net::FakeURLFetcher> ConfigParserTest::CreateFakeURLFetcher(
252    const GURL& url,
253    net::URLFetcherDelegate* fetcher_delegate,
254    const std::string& response_data,
255    net::HttpStatusCode response_code,
256    net::URLRequestStatus::Status status) {
257  request_listener_.real_delegate = fetcher_delegate;
258  scoped_ptr<net::FakeURLFetcher> fetcher(
259      new net::FakeURLFetcher(
260          url, &request_listener_, response_data, response_code, status));
261  scoped_refptr<net::HttpResponseHeaders> download_headers =
262      new net::HttpResponseHeaders("");
263  download_headers->AddHeader("Content-Type: text/xml");
264  fetcher->set_response_headers(download_headers);
265  return fetcher.Pass();
266}
267
268// A helper class to create/delete/check a Chrome desktop shortcut on Windows.
269class ShortcutHandler {
270 public:
271  ShortcutHandler();
272  ~ShortcutHandler();
273
274  static bool IsSupported();
275  ShortcutCommand CreateWithArguments(const base::string16& name,
276                                      const base::string16& args);
277  void CheckShortcutHasArguments(const base::string16& desired_args) const;
278  void Delete();
279
280 private:
281#if defined(OS_WIN)
282  base::FilePath shortcut_path_;
283#endif
284  DISALLOW_COPY_AND_ASSIGN(ShortcutHandler);
285};
286
287#if defined(OS_WIN)
288ShortcutHandler::ShortcutHandler() {
289}
290
291ShortcutHandler::~ShortcutHandler() {
292  if (!shortcut_path_.empty())
293    Delete();
294}
295
296// static
297bool ShortcutHandler::IsSupported() {
298  return true;
299}
300
301ShortcutCommand ShortcutHandler::CreateWithArguments(
302    const base::string16& name,
303    const base::string16& args) {
304  EXPECT_TRUE(shortcut_path_.empty());
305  base::FilePath path_to_create;
306  EXPECT_TRUE(PathService::Get(base::DIR_USER_DESKTOP, &path_to_create));
307  path_to_create = path_to_create.Append(name);
308  EXPECT_FALSE(base::PathExists(path_to_create)) << path_to_create.value();
309
310  base::FilePath path_exe;
311  EXPECT_TRUE(PathService::Get(base::FILE_EXE, &path_exe));
312  base::win::ShortcutProperties shortcut_properties;
313  shortcut_properties.set_target(path_exe);
314  shortcut_properties.set_arguments(args);
315  EXPECT_TRUE(base::win::CreateOrUpdateShortcutLink(
316      path_to_create, shortcut_properties,
317      base::win::SHORTCUT_CREATE_ALWAYS)) << path_to_create.value();
318  shortcut_path_ = path_to_create;
319  return ShortcutCommand(shortcut_path_, args);
320}
321
322void ShortcutHandler::CheckShortcutHasArguments(
323    const base::string16& desired_args) const {
324  EXPECT_FALSE(shortcut_path_.empty());
325  base::string16 args;
326  EXPECT_TRUE(base::win::ResolveShortcut(shortcut_path_, NULL, &args));
327  EXPECT_EQ(desired_args, args);
328}
329
330void ShortcutHandler::Delete() {
331  EXPECT_FALSE(shortcut_path_.empty());
332  EXPECT_TRUE(base::DeleteFile(shortcut_path_, false));
333  shortcut_path_.clear();
334}
335#else
336ShortcutHandler::ShortcutHandler() {}
337
338ShortcutHandler::~ShortcutHandler() {}
339
340// static
341bool ShortcutHandler::IsSupported() {
342  return false;
343}
344
345ShortcutCommand ShortcutHandler::CreateWithArguments(
346    const base::string16& name,
347    const base::string16& args) {
348  return ShortcutCommand();
349}
350
351void ShortcutHandler::CheckShortcutHasArguments(
352    const base::string16& desired_args) const {
353}
354
355void ShortcutHandler::Delete() {
356}
357#endif  // defined(OS_WIN)
358
359
360// helper functions -----------------------------------------------------------
361
362scoped_refptr<Extension> CreateExtension(const base::string16& name,
363                                         const base::FilePath& path,
364                                         Manifest::Location location,
365                                         extensions::Manifest::Type type,
366                                         bool installed_by_default) {
367  base::DictionaryValue manifest;
368  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
369  manifest.SetString(extensions::manifest_keys::kName, name);
370  switch (type) {
371    case extensions::Manifest::TYPE_THEME:
372      manifest.Set(extensions::manifest_keys::kTheme,
373                   new base::DictionaryValue);
374      break;
375    case extensions::Manifest::TYPE_HOSTED_APP:
376      manifest.SetString(extensions::manifest_keys::kLaunchWebURL,
377                         "http://www.google.com");
378      manifest.SetString(extensions::manifest_keys::kUpdateURL,
379                         "http://clients2.google.com/service/update2/crx");
380      break;
381    case extensions::Manifest::TYPE_EXTENSION:
382      // do nothing
383      break;
384    default:
385      NOTREACHED();
386  }
387  manifest.SetString(extensions::manifest_keys::kOmniboxKeyword, name);
388  std::string error;
389  scoped_refptr<Extension> extension = Extension::Create(
390      path,
391      location,
392      manifest,
393      installed_by_default ? Extension::WAS_INSTALLED_BY_DEFAULT
394                           : Extension::NO_FLAGS,
395      &error);
396  EXPECT_TRUE(extension.get() != NULL) << error;
397  return extension;
398}
399
400void ReplaceString(std::string* str,
401                   const std::string& placeholder,
402                   const std::string& substitution) {
403  ASSERT_NE(static_cast<std::string*>(NULL), str);
404  size_t placeholder_pos = str->find(placeholder);
405  ASSERT_NE(std::string::npos, placeholder_pos);
406  str->replace(placeholder_pos, placeholder.size(), substitution);
407}
408
409
410/********************* Tests *********************/
411
412TEST_F(ProfileResetterTest, ResetNothing) {
413  // The callback should be called even if there is nothing to reset.
414  ResetAndWait(0);
415}
416
417TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) {
418  PrefService* prefs = profile()->GetPrefs();
419  DCHECK(prefs);
420  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
421
422  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE, kDistributionConfig);
423
424  TemplateURLService* model =
425      TemplateURLServiceFactory::GetForProfile(profile());
426  TemplateURL* default_engine = model->GetDefaultSearchProvider();
427  ASSERT_NE(static_cast<TemplateURL*>(NULL), default_engine);
428  EXPECT_EQ(base::ASCIIToUTF16("first"), default_engine->short_name());
429  EXPECT_EQ(base::ASCIIToUTF16("firstkey"), default_engine->keyword());
430  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", default_engine->url());
431
432  EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL));
433}
434
435TEST_F(ProfileResetterTest, ResetDefaultSearchEnginePartially) {
436  // Search engine's logic is tested by
437  // TemplateURLServiceTest.RepairPrepopulatedSearchEngines.
438  PrefService* prefs = profile()->GetPrefs();
439  DCHECK(prefs);
440  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
441
442  // Make sure TemplateURLService has loaded.
443  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
444
445  TemplateURLService* model =
446      TemplateURLServiceFactory::GetForProfile(profile());
447  TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs();
448
449  // The second call should produce no effect.
450  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
451
452  EXPECT_EQ(urls, model->GetTemplateURLs());
453  EXPECT_EQ(std::string(), prefs->GetString(prefs::kLastPromptedGoogleURL));
454}
455
456TEST_F(ProfileResetterTest, ResetHomepageNonOrganic) {
457  PrefService* prefs = profile()->GetPrefs();
458  DCHECK(prefs);
459  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, true);
460  prefs->SetString(prefs::kHomePage, "http://google.com");
461  prefs->SetBoolean(prefs::kShowHomeButton, false);
462
463  ResetAndWait(ProfileResetter::HOMEPAGE, kDistributionConfig);
464
465  EXPECT_FALSE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
466  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
467  EXPECT_TRUE(prefs->GetBoolean(prefs::kShowHomeButton));
468}
469
470TEST_F(ProfileResetterTest, ResetHomepagePartially) {
471  PrefService* prefs = profile()->GetPrefs();
472  DCHECK(prefs);
473  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
474  prefs->SetString(prefs::kHomePage, "http://www.foo.com");
475  prefs->SetBoolean(prefs::kShowHomeButton, true);
476
477  ResetAndWait(ProfileResetter::HOMEPAGE);
478
479  EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
480  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
481  EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton));
482}
483
484TEST_F(ProfileResetterTest, ResetContentSettings) {
485  HostContentSettingsMap* host_content_settings_map =
486      profile()->GetHostContentSettingsMap();
487  DesktopNotificationService* notification_service =
488      DesktopNotificationServiceFactory::GetForProfile(profile());
489  ContentSettingsPattern pattern =
490      ContentSettingsPattern::FromString("[*.]example.org");
491  std::map<ContentSettingsType, ContentSetting> default_settings;
492
493  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
494    if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
495      notification_service->SetDefaultContentSetting(CONTENT_SETTING_BLOCK);
496      notification_service->GrantPermission(GURL("http://foo.de"));
497    } else if (type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
498               type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
499               type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
500      // These types are excluded because one can't call
501      // GetDefaultContentSetting() for them.
502    } else {
503      ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
504      ContentSetting default_setting =
505          host_content_settings_map->GetDefaultContentSetting(content_type,
506                                                              NULL);
507      default_settings[content_type] = default_setting;
508      ContentSetting wildcard_setting =
509          default_setting == CONTENT_SETTING_BLOCK ? CONTENT_SETTING_ALLOW
510                                                   : CONTENT_SETTING_BLOCK;
511      ContentSetting site_setting =
512          default_setting == CONTENT_SETTING_ALLOW ? CONTENT_SETTING_ALLOW
513                                                   : CONTENT_SETTING_BLOCK;
514      if (HostContentSettingsMap::IsSettingAllowedForType(
515              profile()->GetPrefs(),
516              wildcard_setting,
517              content_type)) {
518        host_content_settings_map->SetDefaultContentSetting(
519            content_type,
520            wildcard_setting);
521      }
522      if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) &&
523          HostContentSettingsMap::IsSettingAllowedForType(
524              profile()->GetPrefs(),
525              site_setting,
526              content_type)) {
527        host_content_settings_map->SetContentSetting(
528            pattern,
529            ContentSettingsPattern::Wildcard(),
530            content_type,
531            std::string(),
532            site_setting);
533        ContentSettingsForOneType host_settings;
534        host_content_settings_map->GetSettingsForOneType(
535            content_type, std::string(), &host_settings);
536        EXPECT_EQ(2U, host_settings.size());
537      }
538    }
539  }
540
541  ResetAndWait(ProfileResetter::CONTENT_SETTINGS);
542
543  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
544    if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
545      EXPECT_EQ(CONTENT_SETTING_ASK,
546                notification_service->GetDefaultContentSetting(NULL));
547      EXPECT_EQ(CONTENT_SETTING_ASK,
548                notification_service->GetContentSetting(GURL("http://foo.de")));
549    } else {
550      ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
551      if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) ||
552          type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
553          content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
554          content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS)
555        continue;
556      ContentSetting default_setting =
557          host_content_settings_map->GetDefaultContentSetting(content_type,
558                                                              NULL);
559      EXPECT_TRUE(default_settings.count(content_type));
560      EXPECT_EQ(default_settings[content_type], default_setting);
561      if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
562        ContentSetting site_setting =
563            host_content_settings_map->GetContentSetting(
564                GURL("example.org"),
565                GURL(),
566                content_type,
567                std::string());
568        EXPECT_EQ(default_setting, site_setting);
569      }
570
571      ContentSettingsForOneType host_settings;
572      host_content_settings_map->GetSettingsForOneType(
573          content_type, std::string(), &host_settings);
574      EXPECT_EQ(1U, host_settings.size());
575    }
576  }
577}
578
579TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) {
580  service_->Init();
581
582  base::ScopedTempDir temp_dir;
583  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
584
585  scoped_refptr<Extension> theme =
586      CreateExtension(base::ASCIIToUTF16("example1"),
587                      temp_dir.path(),
588                      Manifest::INVALID_LOCATION,
589                      extensions::Manifest::TYPE_THEME,
590                      false);
591  service_->FinishInstallationForTest(theme.get());
592  // Let ThemeService finish creating the theme pack.
593  base::MessageLoop::current()->RunUntilIdle();
594
595  ThemeService* theme_service =
596      ThemeServiceFactory::GetForProfile(profile());
597  EXPECT_FALSE(theme_service->UsingDefaultTheme());
598
599  scoped_refptr<Extension> ext2 = CreateExtension(
600      base::ASCIIToUTF16("example2"),
601      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
602      Manifest::INVALID_LOCATION,
603      extensions::Manifest::TYPE_EXTENSION,
604      false);
605  service_->AddExtension(ext2.get());
606  // Component extensions and policy-managed extensions shouldn't be disabled.
607  scoped_refptr<Extension> ext3 = CreateExtension(
608      base::ASCIIToUTF16("example3"),
609      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
610      Manifest::COMPONENT,
611      extensions::Manifest::TYPE_EXTENSION,
612      false);
613  service_->AddExtension(ext3.get());
614  scoped_refptr<Extension> ext4 =
615      CreateExtension(base::ASCIIToUTF16("example4"),
616                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
617                      Manifest::EXTERNAL_POLICY_DOWNLOAD,
618                      extensions::Manifest::TYPE_EXTENSION,
619                      false);
620  service_->AddExtension(ext4.get());
621  scoped_refptr<Extension> ext5 = CreateExtension(
622      base::ASCIIToUTF16("example5"),
623      base::FilePath(FILE_PATH_LITERAL("//nonexistent4")),
624      Manifest::EXTERNAL_COMPONENT,
625      extensions::Manifest::TYPE_EXTENSION,
626      false);
627  service_->AddExtension(ext5.get());
628  scoped_refptr<Extension> ext6 = CreateExtension(
629      base::ASCIIToUTF16("example6"),
630      base::FilePath(FILE_PATH_LITERAL("//nonexistent5")),
631      Manifest::EXTERNAL_POLICY,
632      extensions::Manifest::TYPE_EXTENSION,
633      false);
634  service_->AddExtension(ext6.get());
635  EXPECT_EQ(6u, service_->extensions()->size());
636
637  ResetAndWait(ProfileResetter::EXTENSIONS);
638  EXPECT_EQ(4u, service_->extensions()->size());
639  EXPECT_FALSE(service_->extensions()->Contains(theme->id()));
640  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
641  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
642  EXPECT_TRUE(service_->extensions()->Contains(ext4->id()));
643  EXPECT_TRUE(service_->extensions()->Contains(ext5->id()));
644  EXPECT_TRUE(service_->extensions()->Contains(ext6->id()));
645  EXPECT_TRUE(theme_service->UsingDefaultTheme());
646}
647
648TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) {
649  scoped_refptr<Extension> ext2 = CreateExtension(
650      base::ASCIIToUTF16("example2"),
651      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
652      Manifest::INVALID_LOCATION,
653      extensions::Manifest::TYPE_EXTENSION,
654      false);
655  service_->AddExtension(ext2.get());
656  // Components and external policy extensions shouldn't be deleted.
657  scoped_refptr<Extension> ext3 = CreateExtension(
658      base::ASCIIToUTF16("example3"),
659      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
660      Manifest::INVALID_LOCATION,
661      extensions::Manifest::TYPE_EXTENSION,
662      false);
663  service_->AddExtension(ext3.get());
664  EXPECT_EQ(2u, service_->extensions()->size());
665
666  std::string master_prefs(kDistributionConfig);
667  ReplaceString(&master_prefs, "placeholder_for_id", ext3->id());
668
669  ResetAndWait(ProfileResetter::EXTENSIONS, master_prefs);
670
671  EXPECT_EQ(1u, service_->extensions()->size());
672  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
673}
674
675TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) {
676  service_->Init();
677
678  base::ScopedTempDir temp_dir;
679  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
680
681  scoped_refptr<Extension> ext1 =
682      CreateExtension(base::ASCIIToUTF16("example1"),
683                      temp_dir.path(),
684                      Manifest::INVALID_LOCATION,
685                      extensions::Manifest::TYPE_THEME,
686                      false);
687  service_->FinishInstallationForTest(ext1.get());
688  // Let ThemeService finish creating the theme pack.
689  base::MessageLoop::current()->RunUntilIdle();
690
691  ThemeService* theme_service =
692      ThemeServiceFactory::GetForProfile(profile());
693  EXPECT_FALSE(theme_service->UsingDefaultTheme());
694
695  scoped_refptr<Extension> ext2 =
696      CreateExtension(base::ASCIIToUTF16("example2"),
697                      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
698                      Manifest::INVALID_LOCATION,
699                      extensions::Manifest::TYPE_EXTENSION,
700                      false);
701  service_->AddExtension(ext2.get());
702
703  scoped_refptr<Extension> ext3 =
704      CreateExtension(base::ASCIIToUTF16("example2"),
705                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
706                      Manifest::INVALID_LOCATION,
707                      extensions::Manifest::TYPE_HOSTED_APP,
708                      true);
709  service_->AddExtension(ext3.get());
710  EXPECT_EQ(3u, service_->extensions()->size());
711
712  ResetAndWait(ProfileResetter::EXTENSIONS);
713
714  EXPECT_EQ(1u, service_->extensions()->size());
715  EXPECT_FALSE(service_->extensions()->Contains(ext1->id()));
716  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
717  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
718  EXPECT_TRUE(theme_service->UsingDefaultTheme());
719}
720
721TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) {
722  PrefService* prefs = profile()->GetPrefs();
723  DCHECK(prefs);
724
725  SessionStartupPref startup_pref(SessionStartupPref::LAST);
726  SessionStartupPref::SetStartupPref(prefs, startup_pref);
727
728  ResetAndWait(ProfileResetter::STARTUP_PAGES, kDistributionConfig);
729
730  startup_pref = SessionStartupPref::GetStartupPref(prefs);
731  EXPECT_EQ(SessionStartupPref::URLS, startup_pref.type);
732  const GURL urls[] = {GURL("http://goo.gl"), GURL("http://foo.de")};
733  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
734}
735
736
737TEST_F(ProfileResetterTest, ResetStartPagePartially) {
738  PrefService* prefs = profile()->GetPrefs();
739  DCHECK(prefs);
740
741  const GURL urls[] = {GURL("http://foo"), GURL("http://bar")};
742  SessionStartupPref startup_pref(SessionStartupPref::URLS);
743  startup_pref.urls.assign(urls, urls + arraysize(urls));
744  SessionStartupPref::SetStartupPref(prefs, startup_pref);
745
746  ResetAndWait(ProfileResetter::STARTUP_PAGES, std::string());
747
748  startup_pref = SessionStartupPref::GetStartupPref(prefs);
749  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type);
750  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
751}
752
753TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
754  scoped_refptr<Extension> extension_app = CreateExtension(
755      base::ASCIIToUTF16("hello!"),
756      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
757      Manifest::INVALID_LOCATION,
758      extensions::Manifest::TYPE_HOSTED_APP,
759      false);
760  scoped_ptr<content::WebContents> contents1(CreateWebContents());
761  extensions::TabHelper::CreateForWebContents(contents1.get());
762  extensions::TabHelper::FromWebContents(contents1.get())->
763      SetExtensionApp(extension_app.get());
764  scoped_ptr<content::WebContents> contents2(CreateWebContents());
765  scoped_ptr<content::WebContents> contents3(CreateWebContents());
766  scoped_ptr<content::WebContents> contents4(CreateWebContents());
767  TabStripModel* tab_strip_model = browser()->tab_strip_model();
768
769  tab_strip_model->AppendWebContents(contents4.get(), true);
770  tab_strip_model->AppendWebContents(contents3.get(), true);
771  tab_strip_model->AppendWebContents(contents2.get(), true);
772  tab_strip_model->SetTabPinned(2, true);
773  tab_strip_model->AppendWebContents(contents1.get(), true);
774  tab_strip_model->SetTabPinned(3, true);
775
776  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(0));
777  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(1));
778  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
779  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
780  EXPECT_EQ(3, tab_strip_model->IndexOfFirstNonMiniTab());
781
782  ResetAndWait(ProfileResetter::PINNED_TABS);
783
784  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(0));
785  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(1));
786  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
787  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
788  EXPECT_EQ(1, tab_strip_model->IndexOfFirstNonMiniTab());
789}
790
791TEST_F(ProfileResetterTest, ResetShortcuts) {
792  ShortcutHandler shortcut;
793  ShortcutCommand command_line = shortcut.CreateWithArguments(
794      base::ASCIIToUTF16("chrome.lnk"),
795      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
796  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
797      "--profile-directory=Default foo.com"));
798
799  ResetAndWait(ProfileResetter::SHORTCUTS);
800
801  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
802      "--profile-directory=Default"));
803}
804
805TEST_F(ProfileResetterTest, ResetFewFlags) {
806  // mock_object_ is a StrictMock, so we verify that it is called only once.
807  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
808               ProfileResetter::HOMEPAGE |
809               ProfileResetter::CONTENT_SETTINGS);
810}
811
812// Tries to load unavailable config file.
813TEST_F(ConfigParserTest, NoConnectivity) {
814  const GURL url("http://test");
815  factory().SetFakeResponse(url, "", net::HTTP_INTERNAL_SERVER_ERROR,
816                            net::URLRequestStatus::FAILED);
817
818  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
819  EXPECT_FALSE(fetcher->GetSettings());
820}
821
822// Tries to load available config file.
823TEST_F(ConfigParserTest, ParseConfig) {
824  const GURL url("http://test");
825  std::string xml_config(kXmlConfig);
826  ReplaceString(&xml_config, "placeholder_for_data", kDistributionConfig);
827  ReplaceString(&xml_config,
828                "placeholder_for_id",
829                "abbaabbaabbaabbaabbaabbaabbaabba");
830  factory().SetFakeResponse(url, xml_config, net::HTTP_OK,
831                            net::URLRequestStatus::SUCCESS);
832
833  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
834  scoped_ptr<BrandcodedDefaultSettings> settings = fetcher->GetSettings();
835  ASSERT_TRUE(settings);
836
837  std::vector<std::string> extension_ids;
838  EXPECT_TRUE(settings->GetExtensions(&extension_ids));
839  EXPECT_EQ(1u, extension_ids.size());
840  EXPECT_EQ("abbaabbaabbaabbaabbaabbaabbaabba", extension_ids[0]);
841
842  std::string homepage;
843  EXPECT_TRUE(settings->GetHomepage(&homepage));
844  EXPECT_EQ("http://www.foo.com", homepage);
845
846  scoped_ptr<base::ListValue> startup_list(
847      settings->GetUrlsToRestoreOnStartup());
848  EXPECT_TRUE(startup_list);
849  std::vector<std::string> startup_pages;
850  for (base::ListValue::iterator i = startup_list->begin();
851       i != startup_list->end(); ++i) {
852    std::string url;
853    EXPECT_TRUE((*i)->GetAsString(&url));
854    startup_pages.push_back(url);
855  }
856  ASSERT_EQ(2u, startup_pages.size());
857  EXPECT_EQ("http://goo.gl", startup_pages[0]);
858  EXPECT_EQ("http://foo.de", startup_pages[1]);
859}
860
861TEST_F(ProfileResetterTest, CheckSnapshots) {
862  ResettableSettingsSnapshot empty_snap(profile());
863  EXPECT_EQ(0, empty_snap.FindDifferentFields(empty_snap));
864
865  scoped_refptr<Extension> ext = CreateExtension(
866      base::ASCIIToUTF16("example"),
867      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
868      Manifest::INVALID_LOCATION,
869      extensions::Manifest::TYPE_EXTENSION,
870      false);
871  ASSERT_TRUE(ext);
872  service_->AddExtension(ext.get());
873
874  std::string master_prefs(kDistributionConfig);
875  std::string ext_id = ext->id();
876  ReplaceString(&master_prefs, "placeholder_for_id", ext_id);
877
878  // Reset to non organic defaults.
879  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
880               ProfileResetter::HOMEPAGE |
881               ProfileResetter::STARTUP_PAGES,
882               master_prefs);
883  ShortcutHandler shortcut_hijacked;
884  ShortcutCommand command_line = shortcut_hijacked.CreateWithArguments(
885      base::ASCIIToUTF16("chrome1.lnk"),
886      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
887  shortcut_hijacked.CheckShortcutHasArguments(
888      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
889  ShortcutHandler shortcut_ok;
890  shortcut_ok.CreateWithArguments(
891      base::ASCIIToUTF16("chrome2.lnk"),
892      base::ASCIIToUTF16("--profile-directory=Default1"));
893
894  ResettableSettingsSnapshot nonorganic_snap(profile());
895  nonorganic_snap.RequestShortcuts(base::Closure());
896  // Let it enumerate shortcuts on the FILE thread.
897  base::MessageLoop::current()->RunUntilIdle();
898  int diff_fields = ResettableSettingsSnapshot::ALL_FIELDS;
899  if (!ShortcutHandler::IsSupported())
900    diff_fields &= ~ResettableSettingsSnapshot::SHORTCUTS;
901  EXPECT_EQ(diff_fields,
902            empty_snap.FindDifferentFields(nonorganic_snap));
903  empty_snap.Subtract(nonorganic_snap);
904  EXPECT_TRUE(empty_snap.startup_urls().empty());
905  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(),
906            empty_snap.startup_type());
907  EXPECT_TRUE(empty_snap.homepage().empty());
908  EXPECT_TRUE(empty_snap.homepage_is_ntp());
909  EXPECT_NE(std::string::npos, empty_snap.dse_url().find("{google:baseURL}"));
910  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(),
911            empty_snap.enabled_extensions());
912  EXPECT_EQ(std::vector<ShortcutCommand>(), empty_snap.shortcuts());
913
914  // Reset to organic defaults.
915  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
916               ProfileResetter::HOMEPAGE |
917               ProfileResetter::STARTUP_PAGES |
918               ProfileResetter::EXTENSIONS |
919               ProfileResetter::SHORTCUTS);
920
921  ResettableSettingsSnapshot organic_snap(profile());
922  organic_snap.RequestShortcuts(base::Closure());
923  // Let it enumerate shortcuts on the FILE thread.
924  base::MessageLoop::current()->RunUntilIdle();
925  EXPECT_EQ(diff_fields, nonorganic_snap.FindDifferentFields(organic_snap));
926  nonorganic_snap.Subtract(organic_snap);
927  const GURL urls[] = {GURL("http://foo.de"), GURL("http://goo.gl")};
928  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)),
929            nonorganic_snap.startup_urls());
930  EXPECT_EQ(SessionStartupPref::URLS, nonorganic_snap.startup_type());
931  EXPECT_EQ("http://www.foo.com", nonorganic_snap.homepage());
932  EXPECT_FALSE(nonorganic_snap.homepage_is_ntp());
933  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", nonorganic_snap.dse_url());
934  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(
935      1, std::make_pair(ext_id, "example")),
936      nonorganic_snap.enabled_extensions());
937  if (ShortcutHandler::IsSupported()) {
938    std::vector<ShortcutCommand> shortcuts = nonorganic_snap.shortcuts();
939    ASSERT_EQ(1u, shortcuts.size());
940    EXPECT_EQ(command_line.first.value(), shortcuts[0].first.value());
941    EXPECT_EQ(command_line.second, shortcuts[0].second);
942  }
943}
944
945TEST_F(ProfileResetterTest, FeedbackSerializationTest) {
946  // Reset to non organic defaults.
947  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
948               ProfileResetter::HOMEPAGE |
949               ProfileResetter::STARTUP_PAGES,
950               kDistributionConfig);
951
952  scoped_refptr<Extension> ext = CreateExtension(
953      base::ASCIIToUTF16("example"),
954      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
955      Manifest::INVALID_LOCATION,
956      extensions::Manifest::TYPE_EXTENSION,
957      false);
958  ASSERT_TRUE(ext);
959  service_->AddExtension(ext.get());
960
961  ShortcutHandler shortcut;
962  ShortcutCommand command_line = shortcut.CreateWithArguments(
963      base::ASCIIToUTF16("chrome.lnk"),
964      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
965
966  ResettableSettingsSnapshot nonorganic_snap(profile());
967  nonorganic_snap.RequestShortcuts(base::Closure());
968  // Let it enumerate shortcuts on the FILE thread.
969  base::MessageLoop::current()->RunUntilIdle();
970
971  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
972                 expand_this_test);
973  for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS;
974       ++field_mask) {
975    std::string report = SerializeSettingsReport(nonorganic_snap, field_mask);
976    JSONStringValueSerializer json(report);
977    std::string error;
978    scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
979    ASSERT_TRUE(root) << error;
980    ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY)) << error;
981
982    base::DictionaryValue* dict =
983        static_cast<base::DictionaryValue*>(root.get());
984
985    base::ListValue* startup_urls = NULL;
986    int startup_type = 0;
987    std::string homepage;
988    bool homepage_is_ntp = true;
989    std::string default_search_engine;
990    base::ListValue* extensions = NULL;
991    base::ListValue* shortcuts = NULL;
992
993    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
994              dict->GetList("startup_urls", &startup_urls));
995    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
996              dict->GetInteger("startup_type", &startup_type));
997    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
998              dict->GetString("homepage", &homepage));
999    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
1000              dict->GetBoolean("homepage_is_ntp", &homepage_is_ntp));
1001    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::DSE_URL),
1002              dict->GetString("default_search_engine", &default_search_engine));
1003    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::EXTENSIONS),
1004              dict->GetList("enabled_extensions", &extensions));
1005    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::SHORTCUTS),
1006              dict->GetList("shortcuts", &shortcuts));
1007  }
1008}
1009
1010struct FeedbackCapture {
1011  void SetFeedback(Profile* profile,
1012                   const ResettableSettingsSnapshot& snapshot) {
1013    list_ = GetReadableFeedbackForSnapshot(profile, snapshot).Pass();
1014    OnUpdatedList();
1015  }
1016
1017  void Fail() {
1018    ADD_FAILURE() << "This method shouldn't be called.";
1019  }
1020
1021  MOCK_METHOD0(OnUpdatedList, void(void));
1022
1023  scoped_ptr<base::ListValue> list_;
1024};
1025
1026// Make sure GetReadableFeedback handles non-ascii letters.
1027TEST_F(ProfileResetterTest, GetReadableFeedback) {
1028  scoped_refptr<Extension> ext = CreateExtension(
1029      base::WideToUTF16(L"Tiësto"),
1030      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
1031      Manifest::INVALID_LOCATION,
1032      extensions::Manifest::TYPE_EXTENSION,
1033      false);
1034  ASSERT_TRUE(ext);
1035  service_->AddExtension(ext.get());
1036
1037  PrefService* prefs = profile()->GetPrefs();
1038  DCHECK(prefs);
1039  // The URL is "http://россия.рф".
1040  std::wstring url(L"http://"
1041    L"\u0440\u043e\u0441\u0441\u0438\u044f.\u0440\u0444");
1042  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
1043  prefs->SetString(prefs::kHomePage, base::WideToUTF8(url));
1044
1045  SessionStartupPref startup_pref(SessionStartupPref::URLS);
1046  startup_pref.urls.push_back(GURL(base::WideToUTF8(url)));
1047  SessionStartupPref::SetStartupPref(prefs, startup_pref);
1048
1049  ShortcutHandler shortcut;
1050  ShortcutCommand command_line = shortcut.CreateWithArguments(
1051      base::ASCIIToUTF16("chrome.lnk"),
1052      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
1053
1054  FeedbackCapture capture;
1055  EXPECT_CALL(capture, OnUpdatedList());
1056  ResettableSettingsSnapshot snapshot(profile());
1057  snapshot.RequestShortcuts(base::Bind(&FeedbackCapture::SetFeedback,
1058                                       base::Unretained(&capture),
1059                                       profile(),
1060                                       base::ConstRef(snapshot)));
1061  // Let it enumerate shortcuts on the FILE thread.
1062  base::MessageLoop::current()->RunUntilIdle();
1063  ::testing::Mock::VerifyAndClearExpectations(&capture);
1064  // The homepage and the startup page are in punycode. They are unreadable.
1065  // Trying to find the extension name.
1066  scoped_ptr<base::ListValue> list = capture.list_.Pass();
1067  ASSERT_TRUE(list);
1068  bool checked_extensions = false;
1069  bool checked_shortcuts = false;
1070  for (size_t i = 0; i < list->GetSize(); ++i) {
1071    base::DictionaryValue* dict = NULL;
1072    ASSERT_TRUE(list->GetDictionary(i, &dict));
1073    std::string value;
1074    ASSERT_TRUE(dict->GetString("key", &value));
1075    if (value == "Extensions") {
1076      base::string16 extensions;
1077      EXPECT_TRUE(dict->GetString("value", &extensions));
1078      EXPECT_EQ(base::WideToUTF16(L"Tiësto"), extensions);
1079      checked_extensions = true;
1080    } else if (value == "Shortcut targets") {
1081      base::string16 targets;
1082      EXPECT_TRUE(dict->GetString("value", &targets));
1083      EXPECT_NE(base::string16::npos,
1084                targets.find(base::ASCIIToUTF16("foo.com"))) << targets;
1085      checked_shortcuts = true;
1086    }
1087  }
1088  EXPECT_TRUE(checked_extensions);
1089  EXPECT_EQ(ShortcutHandler::IsSupported(), checked_shortcuts);
1090}
1091
1092TEST_F(ProfileResetterTest, DestroySnapshotFast) {
1093  FeedbackCapture capture;
1094  scoped_ptr<ResettableSettingsSnapshot> deleted_snapshot(
1095      new ResettableSettingsSnapshot(profile()));
1096  deleted_snapshot->RequestShortcuts(base::Bind(&FeedbackCapture::Fail,
1097                                                base::Unretained(&capture)));
1098  deleted_snapshot.reset();
1099  // Running remaining tasks shouldn't trigger the callback to be called as
1100  // |deleted_snapshot| was deleted before it could run.
1101  base::MessageLoop::current()->RunUntilIdle();
1102}
1103
1104}  // namespace
1105