root/chrome/browser/profile_resetter/profile_resetter_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. profile
  2. taskbar_pins_override_
  3. SetUp
  4. CreateTemplateURLService
  5. SetUp
  6. CreateWebContents
  7. OnURLFetchComplete
  8. factory
  9. factory_
  10. WaitForRequest
  11. CreateFakeURLFetcher
  12. IsSupported
  13. CreateWithArguments
  14. CheckShortcutHasArguments
  15. Delete
  16. IsSupported
  17. CreateWithArguments
  18. CheckShortcutHasArguments
  19. Delete
  20. CreateExtension
  21. ReplaceString
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F
  27. TEST_F
  28. TEST_F
  29. TEST_F
  30. TEST_F
  31. TEST_F
  32. TEST_F
  33. TEST_F
  34. TEST_F
  35. TEST_F
  36. TEST_F
  37. TEST_F
  38. TEST_F
  39. TEST_F
  40. TEST_F
  41. TEST_F

// Copyright (c) 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/profile_resetter/profile_resetter.h"

#include "base/json/json_string_value_serializer.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_path_override.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/extensions/extension_service_unittest.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
#include "chrome/browser/profile_resetter/profile_resetter_test_base.h"
#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_browser_thread.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"

#if defined(OS_WIN)
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/process/process_handle.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/shortcut.h"
#endif


namespace {

const char kDistributionConfig[] = "{"
    " \"homepage\" : \"http://www.foo.com\","
    " \"homepage_is_newtabpage\" : false,"
    " \"browser\" : {"
    "   \"show_home_button\" : true"
    "  },"
    " \"session\" : {"
    "   \"restore_on_startup\" : 4,"
    "   \"startup_urls\" : [\"http://goo.gl\", \"http://foo.de\"]"
    "  },"
    " \"search_provider_overrides\" : ["
    "    {"
    "      \"name\" : \"first\","
    "      \"keyword\" : \"firstkey\","
    "      \"search_url\" : \"http://www.foo.com/s?q={searchTerms}\","
    "      \"favicon_url\" : \"http://www.foo.com/favicon.ico\","
    "      \"suggest_url\" : \"http://www.foo.com/s?q={searchTerms}\","
    "      \"encoding\" : \"UTF-8\","
    "      \"id\" : 1001"
    "    }"
    "  ],"
    " \"extensions\" : {"
    "   \"settings\" : {"
    "     \"placeholder_for_id\": {"
    "      }"
    "    }"
    "  }"
    "}";

const char kXmlConfig[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
    "<response protocol=\"3.0\" server=\"prod\">"
      "<app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\">"
        "<data index=\"skipfirstrunui-importsearch-defaultbrowser\" "
          "name=\"install\" status=\"ok\">"
          "placeholder_for_data"
        "</data>"
      "</app>"
    "</response>";

using extensions::Extension;
using extensions::Manifest;


// ProfileResetterTest --------------------------------------------------------

// ProfileResetterTest sets up the extension, WebData and TemplateURL services.
class ProfileResetterTest : public ExtensionServiceTestBase,
                            public ProfileResetterTestBase {
 public:
  ProfileResetterTest();
  virtual ~ProfileResetterTest();

 protected:
  virtual void SetUp() OVERRIDE;

  TestingProfile* profile() { return profile_.get(); }

  static KeyedService* CreateTemplateURLService(
      content::BrowserContext* context);

 private:
#if defined(OS_WIN)
  base::ScopedPathOverride user_desktop_override_;
  base::ScopedPathOverride app_dir_override_;
  base::ScopedPathOverride start_menu_override_;
  base::ScopedPathOverride taskbar_pins_override_;
  base::win::ScopedCOMInitializer com_init_;
#endif
};

ProfileResetterTest::ProfileResetterTest()
#if defined(OS_WIN)
    : user_desktop_override_(base::DIR_USER_DESKTOP),
      app_dir_override_(base::DIR_APP_DATA),
      start_menu_override_(base::DIR_START_MENU),
      taskbar_pins_override_(base::DIR_TASKBAR_PINS)
#endif
{}

ProfileResetterTest::~ProfileResetterTest() {
}

void ProfileResetterTest::SetUp() {
  ExtensionServiceTestBase::SetUp();
  InitializeEmptyExtensionService();

  profile()->CreateWebDataService();
  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
      profile(),
      &ProfileResetterTest::CreateTemplateURLService);
  resetter_.reset(new ProfileResetter(profile()));
}

// static
KeyedService* ProfileResetterTest::CreateTemplateURLService(
    content::BrowserContext* context) {
  return new TemplateURLService(static_cast<Profile*>(context));
}


// PinnedTabsResetTest --------------------------------------------------------

class PinnedTabsResetTest : public BrowserWithTestWindowTest,
                            public ProfileResetterTestBase {
 protected:
  virtual void SetUp() OVERRIDE;

  content::WebContents* CreateWebContents();
};

void PinnedTabsResetTest::SetUp() {
  BrowserWithTestWindowTest::SetUp();
  resetter_.reset(new ProfileResetter(profile()));
}

content::WebContents* PinnedTabsResetTest::CreateWebContents() {
  return content::WebContents::Create(
      content::WebContents::CreateParams(profile()));
}


// ConfigParserTest -----------------------------------------------------------

// URLFetcher delegate that simply records the upload data.
struct URLFetcherRequestListener : net::URLFetcherDelegate {
  URLFetcherRequestListener();
  virtual ~URLFetcherRequestListener();

  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;

  std::string upload_data;
  net::URLFetcherDelegate* real_delegate;
};

URLFetcherRequestListener::URLFetcherRequestListener()
    : real_delegate(NULL) {
}

URLFetcherRequestListener::~URLFetcherRequestListener() {
}

void URLFetcherRequestListener::OnURLFetchComplete(
    const net::URLFetcher* source) {
  const net::TestURLFetcher* test_fetcher =
      static_cast<const net::TestURLFetcher*>(source);
  upload_data = test_fetcher->upload_data();
  DCHECK(real_delegate);
  real_delegate->OnURLFetchComplete(source);
}

class ConfigParserTest : public testing::Test {
 protected:
  ConfigParserTest();
  virtual ~ConfigParserTest();

  scoped_ptr<BrandcodeConfigFetcher> WaitForRequest(const GURL& url);

  net::FakeURLFetcherFactory& factory() { return factory_; }

 private:
  scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
      const GURL& url,
      net::URLFetcherDelegate* fetcher_delegate,
      const std::string& response_data,
      net::HttpStatusCode response_code,
      net::URLRequestStatus::Status status);

  MOCK_METHOD0(Callback, void(void));

  base::MessageLoopForIO loop_;
  content::TestBrowserThread ui_thread_;
  content::TestBrowserThread io_thread_;
  URLFetcherRequestListener request_listener_;
  net::FakeURLFetcherFactory factory_;
};

ConfigParserTest::ConfigParserTest()
    : ui_thread_(content::BrowserThread::UI, &loop_),
      io_thread_(content::BrowserThread::IO, &loop_),
      factory_(NULL, base::Bind(&ConfigParserTest::CreateFakeURLFetcher,
                                base::Unretained(this))) {
}

ConfigParserTest::~ConfigParserTest() {}

scoped_ptr<BrandcodeConfigFetcher> ConfigParserTest::WaitForRequest(
    const GURL& url) {
  EXPECT_CALL(*this, Callback());
  scoped_ptr<BrandcodeConfigFetcher> fetcher(
      new BrandcodeConfigFetcher(base::Bind(&ConfigParserTest::Callback,
                                            base::Unretained(this)),
                                 url,
                                 "ABCD"));
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_FALSE(fetcher->IsActive());
  // Look for the brand code in the request.
  EXPECT_NE(std::string::npos, request_listener_.upload_data.find("ABCD"));
  return fetcher.Pass();
}

scoped_ptr<net::FakeURLFetcher> ConfigParserTest::CreateFakeURLFetcher(
    const GURL& url,
    net::URLFetcherDelegate* fetcher_delegate,
    const std::string& response_data,
    net::HttpStatusCode response_code,
    net::URLRequestStatus::Status status) {
  request_listener_.real_delegate = fetcher_delegate;
  scoped_ptr<net::FakeURLFetcher> fetcher(
      new net::FakeURLFetcher(
          url, &request_listener_, response_data, response_code, status));
  scoped_refptr<net::HttpResponseHeaders> download_headers =
      new net::HttpResponseHeaders("");
  download_headers->AddHeader("Content-Type: text/xml");
  fetcher->set_response_headers(download_headers);
  return fetcher.Pass();
}

// A helper class to create/delete/check a Chrome desktop shortcut on Windows.
class ShortcutHandler {
 public:
  ShortcutHandler();
  ~ShortcutHandler();

  static bool IsSupported();
  ShortcutCommand CreateWithArguments(const base::string16& name,
                                      const base::string16& args);
  void CheckShortcutHasArguments(const base::string16& desired_args) const;
  void Delete();

 private:
#if defined(OS_WIN)
  base::FilePath shortcut_path_;
#endif
  DISALLOW_COPY_AND_ASSIGN(ShortcutHandler);
};

#if defined(OS_WIN)
ShortcutHandler::ShortcutHandler() {
}

ShortcutHandler::~ShortcutHandler() {
  if (!shortcut_path_.empty())
    Delete();
}

// static
bool ShortcutHandler::IsSupported() {
  return true;
}

ShortcutCommand ShortcutHandler::CreateWithArguments(
    const base::string16& name,
    const base::string16& args) {
  EXPECT_TRUE(shortcut_path_.empty());
  base::FilePath path_to_create;
  EXPECT_TRUE(PathService::Get(base::DIR_USER_DESKTOP, &path_to_create));
  path_to_create = path_to_create.Append(name);
  EXPECT_FALSE(base::PathExists(path_to_create)) << path_to_create.value();

  base::FilePath path_exe;
  EXPECT_TRUE(PathService::Get(base::FILE_EXE, &path_exe));
  base::win::ShortcutProperties shortcut_properties;
  shortcut_properties.set_target(path_exe);
  shortcut_properties.set_arguments(args);
  EXPECT_TRUE(base::win::CreateOrUpdateShortcutLink(
      path_to_create, shortcut_properties,
      base::win::SHORTCUT_CREATE_ALWAYS)) << path_to_create.value();
  shortcut_path_ = path_to_create;
  return ShortcutCommand(shortcut_path_, args);
}

void ShortcutHandler::CheckShortcutHasArguments(
    const base::string16& desired_args) const {
  EXPECT_FALSE(shortcut_path_.empty());
  base::string16 args;
  EXPECT_TRUE(base::win::ResolveShortcut(shortcut_path_, NULL, &args));
  EXPECT_EQ(desired_args, args);
}

void ShortcutHandler::Delete() {
  EXPECT_FALSE(shortcut_path_.empty());
  EXPECT_TRUE(base::DeleteFile(shortcut_path_, false));
  shortcut_path_.clear();
}
#else
ShortcutHandler::ShortcutHandler() {}

ShortcutHandler::~ShortcutHandler() {}

// static
bool ShortcutHandler::IsSupported() {
  return false;
}

ShortcutCommand ShortcutHandler::CreateWithArguments(
    const base::string16& name,
    const base::string16& args) {
  return ShortcutCommand();
}

void ShortcutHandler::CheckShortcutHasArguments(
    const base::string16& desired_args) const {
}

void ShortcutHandler::Delete() {
}
#endif  // defined(OS_WIN)


// helper functions -----------------------------------------------------------

scoped_refptr<Extension> CreateExtension(const base::string16& name,
                                         const base::FilePath& path,
                                         Manifest::Location location,
                                         extensions::Manifest::Type type,
                                         bool installed_by_default) {
  base::DictionaryValue manifest;
  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
  manifest.SetString(extensions::manifest_keys::kName, name);
  switch (type) {
    case extensions::Manifest::TYPE_THEME:
      manifest.Set(extensions::manifest_keys::kTheme,
                   new base::DictionaryValue);
      break;
    case extensions::Manifest::TYPE_HOSTED_APP:
      manifest.SetString(extensions::manifest_keys::kLaunchWebURL,
                         "http://www.google.com");
      manifest.SetString(extensions::manifest_keys::kUpdateURL,
                         "http://clients2.google.com/service/update2/crx");
      break;
    case extensions::Manifest::TYPE_EXTENSION:
      // do nothing
      break;
    default:
      NOTREACHED();
  }
  manifest.SetString(extensions::manifest_keys::kOmniboxKeyword, name);
  std::string error;
  scoped_refptr<Extension> extension = Extension::Create(
      path,
      location,
      manifest,
      installed_by_default ? Extension::WAS_INSTALLED_BY_DEFAULT
                           : Extension::NO_FLAGS,
      &error);
  EXPECT_TRUE(extension.get() != NULL) << error;
  return extension;
}

void ReplaceString(std::string* str,
                   const std::string& placeholder,
                   const std::string& substitution) {
  ASSERT_NE(static_cast<std::string*>(NULL), str);
  size_t placeholder_pos = str->find(placeholder);
  ASSERT_NE(std::string::npos, placeholder_pos);
  str->replace(placeholder_pos, placeholder.size(), substitution);
}


/********************* Tests *********************/

TEST_F(ProfileResetterTest, ResetNothing) {
  // The callback should be called even if there is nothing to reset.
  ResetAndWait(0);
}

TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) {
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);
  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");

  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE, kDistributionConfig);

  TemplateURLService* model =
      TemplateURLServiceFactory::GetForProfile(profile());
  TemplateURL* default_engine = model->GetDefaultSearchProvider();
  ASSERT_NE(static_cast<TemplateURL*>(NULL), default_engine);
  EXPECT_EQ(base::ASCIIToUTF16("first"), default_engine->short_name());
  EXPECT_EQ(base::ASCIIToUTF16("firstkey"), default_engine->keyword());
  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", default_engine->url());

  EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL));
}

TEST_F(ProfileResetterTest, ResetDefaultSearchEnginePartially) {
  // Search engine's logic is tested by
  // TemplateURLServiceTest.RepairPrepopulatedSearchEngines.
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);
  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");

  // Make sure TemplateURLService has loaded.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);

  TemplateURLService* model =
      TemplateURLServiceFactory::GetForProfile(profile());
  TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs();

  // The second call should produce no effect.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);

  EXPECT_EQ(urls, model->GetTemplateURLs());
  EXPECT_EQ(std::string(), prefs->GetString(prefs::kLastPromptedGoogleURL));
}

TEST_F(ProfileResetterTest, ResetHomepageNonOrganic) {
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);
  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, true);
  prefs->SetString(prefs::kHomePage, "http://google.com");
  prefs->SetBoolean(prefs::kShowHomeButton, false);

  ResetAndWait(ProfileResetter::HOMEPAGE, kDistributionConfig);

  EXPECT_FALSE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
  EXPECT_TRUE(prefs->GetBoolean(prefs::kShowHomeButton));
}

TEST_F(ProfileResetterTest, ResetHomepagePartially) {
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);
  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
  prefs->SetString(prefs::kHomePage, "http://www.foo.com");
  prefs->SetBoolean(prefs::kShowHomeButton, true);

  ResetAndWait(ProfileResetter::HOMEPAGE);

  EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
  EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton));
}

TEST_F(ProfileResetterTest, ResetContentSettings) {
  HostContentSettingsMap* host_content_settings_map =
      profile()->GetHostContentSettingsMap();
  DesktopNotificationService* notification_service =
      DesktopNotificationServiceFactory::GetForProfile(profile());
  ContentSettingsPattern pattern =
      ContentSettingsPattern::FromString("[*.]example.org");
  std::map<ContentSettingsType, ContentSetting> default_settings;

  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
    if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
      notification_service->SetDefaultContentSetting(CONTENT_SETTING_BLOCK);
      notification_service->GrantPermission(GURL("http://foo.de"));
    } else if (type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
               type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
               type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
      // These types are excluded because one can't call
      // GetDefaultContentSetting() for them.
    } else {
      ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
      ContentSetting default_setting =
          host_content_settings_map->GetDefaultContentSetting(content_type,
                                                              NULL);
      default_settings[content_type] = default_setting;
      ContentSetting wildcard_setting =
          default_setting == CONTENT_SETTING_BLOCK ? CONTENT_SETTING_ALLOW
                                                   : CONTENT_SETTING_BLOCK;
      ContentSetting site_setting =
          default_setting == CONTENT_SETTING_ALLOW ? CONTENT_SETTING_ALLOW
                                                   : CONTENT_SETTING_BLOCK;
      if (HostContentSettingsMap::IsSettingAllowedForType(
              profile()->GetPrefs(),
              wildcard_setting,
              content_type)) {
        host_content_settings_map->SetDefaultContentSetting(
            content_type,
            wildcard_setting);
      }
      if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) &&
          HostContentSettingsMap::IsSettingAllowedForType(
              profile()->GetPrefs(),
              site_setting,
              content_type)) {
        host_content_settings_map->SetContentSetting(
            pattern,
            ContentSettingsPattern::Wildcard(),
            content_type,
            std::string(),
            site_setting);
        ContentSettingsForOneType host_settings;
        host_content_settings_map->GetSettingsForOneType(
            content_type, std::string(), &host_settings);
        EXPECT_EQ(2U, host_settings.size());
      }
    }
  }

  ResetAndWait(ProfileResetter::CONTENT_SETTINGS);

  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
    if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
      EXPECT_EQ(CONTENT_SETTING_ASK,
                notification_service->GetDefaultContentSetting(NULL));
      EXPECT_EQ(CONTENT_SETTING_ASK,
                notification_service->GetContentSetting(GURL("http://foo.de")));
    } else {
      ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
      if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) ||
          type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
          content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
          content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS)
        continue;
      ContentSetting default_setting =
          host_content_settings_map->GetDefaultContentSetting(content_type,
                                                              NULL);
      EXPECT_TRUE(default_settings.count(content_type));
      EXPECT_EQ(default_settings[content_type], default_setting);
      if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
        ContentSetting site_setting =
            host_content_settings_map->GetContentSetting(
                GURL("example.org"),
                GURL(),
                content_type,
                std::string());
        EXPECT_EQ(default_setting, site_setting);
      }

      ContentSettingsForOneType host_settings;
      host_content_settings_map->GetSettingsForOneType(
          content_type, std::string(), &host_settings);
      EXPECT_EQ(1U, host_settings.size());
    }
  }
}

TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) {
  service_->Init();

  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  scoped_refptr<Extension> theme =
      CreateExtension(base::ASCIIToUTF16("example1"),
                      temp_dir.path(),
                      Manifest::INVALID_LOCATION,
                      extensions::Manifest::TYPE_THEME,
                      false);
  service_->FinishInstallationForTest(theme.get());
  // Let ThemeService finish creating the theme pack.
  base::MessageLoop::current()->RunUntilIdle();

  ThemeService* theme_service =
      ThemeServiceFactory::GetForProfile(profile());
  EXPECT_FALSE(theme_service->UsingDefaultTheme());

  scoped_refptr<Extension> ext2 = CreateExtension(
      base::ASCIIToUTF16("example2"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext2.get());
  // Component extensions and policy-managed extensions shouldn't be disabled.
  scoped_refptr<Extension> ext3 = CreateExtension(
      base::ASCIIToUTF16("example3"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
      Manifest::COMPONENT,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext3.get());
  scoped_refptr<Extension> ext4 =
      CreateExtension(base::ASCIIToUTF16("example4"),
                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
                      Manifest::EXTERNAL_POLICY_DOWNLOAD,
                      extensions::Manifest::TYPE_EXTENSION,
                      false);
  service_->AddExtension(ext4.get());
  scoped_refptr<Extension> ext5 = CreateExtension(
      base::ASCIIToUTF16("example5"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent4")),
      Manifest::EXTERNAL_COMPONENT,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext5.get());
  scoped_refptr<Extension> ext6 = CreateExtension(
      base::ASCIIToUTF16("example6"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent5")),
      Manifest::EXTERNAL_POLICY,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext6.get());
  EXPECT_EQ(6u, service_->extensions()->size());

  ResetAndWait(ProfileResetter::EXTENSIONS);
  EXPECT_EQ(4u, service_->extensions()->size());
  EXPECT_FALSE(service_->extensions()->Contains(theme->id()));
  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
  EXPECT_TRUE(service_->extensions()->Contains(ext4->id()));
  EXPECT_TRUE(service_->extensions()->Contains(ext5->id()));
  EXPECT_TRUE(service_->extensions()->Contains(ext6->id()));
  EXPECT_TRUE(theme_service->UsingDefaultTheme());
}

TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) {
  scoped_refptr<Extension> ext2 = CreateExtension(
      base::ASCIIToUTF16("example2"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext2.get());
  // Components and external policy extensions shouldn't be deleted.
  scoped_refptr<Extension> ext3 = CreateExtension(
      base::ASCIIToUTF16("example3"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  service_->AddExtension(ext3.get());
  EXPECT_EQ(2u, service_->extensions()->size());

  std::string master_prefs(kDistributionConfig);
  ReplaceString(&master_prefs, "placeholder_for_id", ext3->id());

  ResetAndWait(ProfileResetter::EXTENSIONS, master_prefs);

  EXPECT_EQ(1u, service_->extensions()->size());
  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
}

TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) {
  service_->Init();

  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  scoped_refptr<Extension> ext1 =
      CreateExtension(base::ASCIIToUTF16("example1"),
                      temp_dir.path(),
                      Manifest::INVALID_LOCATION,
                      extensions::Manifest::TYPE_THEME,
                      false);
  service_->FinishInstallationForTest(ext1.get());
  // Let ThemeService finish creating the theme pack.
  base::MessageLoop::current()->RunUntilIdle();

  ThemeService* theme_service =
      ThemeServiceFactory::GetForProfile(profile());
  EXPECT_FALSE(theme_service->UsingDefaultTheme());

  scoped_refptr<Extension> ext2 =
      CreateExtension(base::ASCIIToUTF16("example2"),
                      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
                      Manifest::INVALID_LOCATION,
                      extensions::Manifest::TYPE_EXTENSION,
                      false);
  service_->AddExtension(ext2.get());

  scoped_refptr<Extension> ext3 =
      CreateExtension(base::ASCIIToUTF16("example2"),
                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
                      Manifest::INVALID_LOCATION,
                      extensions::Manifest::TYPE_HOSTED_APP,
                      true);
  service_->AddExtension(ext3.get());
  EXPECT_EQ(3u, service_->extensions()->size());

  ResetAndWait(ProfileResetter::EXTENSIONS);

  EXPECT_EQ(1u, service_->extensions()->size());
  EXPECT_FALSE(service_->extensions()->Contains(ext1->id()));
  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
  EXPECT_TRUE(theme_service->UsingDefaultTheme());
}

TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) {
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);

  SessionStartupPref startup_pref(SessionStartupPref::LAST);
  SessionStartupPref::SetStartupPref(prefs, startup_pref);

  ResetAndWait(ProfileResetter::STARTUP_PAGES, kDistributionConfig);

  startup_pref = SessionStartupPref::GetStartupPref(prefs);
  EXPECT_EQ(SessionStartupPref::URLS, startup_pref.type);
  const GURL urls[] = {GURL("http://goo.gl"), GURL("http://foo.de")};
  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
}


TEST_F(ProfileResetterTest, ResetStartPagePartially) {
  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);

  const GURL urls[] = {GURL("http://foo"), GURL("http://bar")};
  SessionStartupPref startup_pref(SessionStartupPref::URLS);
  startup_pref.urls.assign(urls, urls + arraysize(urls));
  SessionStartupPref::SetStartupPref(prefs, startup_pref);

  ResetAndWait(ProfileResetter::STARTUP_PAGES, std::string());

  startup_pref = SessionStartupPref::GetStartupPref(prefs);
  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type);
  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
}

TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
  scoped_refptr<Extension> extension_app = CreateExtension(
      base::ASCIIToUTF16("hello!"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_HOSTED_APP,
      false);
  scoped_ptr<content::WebContents> contents1(CreateWebContents());
  extensions::TabHelper::CreateForWebContents(contents1.get());
  extensions::TabHelper::FromWebContents(contents1.get())->
      SetExtensionApp(extension_app.get());
  scoped_ptr<content::WebContents> contents2(CreateWebContents());
  scoped_ptr<content::WebContents> contents3(CreateWebContents());
  scoped_ptr<content::WebContents> contents4(CreateWebContents());
  TabStripModel* tab_strip_model = browser()->tab_strip_model();

  tab_strip_model->AppendWebContents(contents4.get(), true);
  tab_strip_model->AppendWebContents(contents3.get(), true);
  tab_strip_model->AppendWebContents(contents2.get(), true);
  tab_strip_model->SetTabPinned(2, true);
  tab_strip_model->AppendWebContents(contents1.get(), true);
  tab_strip_model->SetTabPinned(3, true);

  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(0));
  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(1));
  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
  EXPECT_EQ(3, tab_strip_model->IndexOfFirstNonMiniTab());

  ResetAndWait(ProfileResetter::PINNED_TABS);

  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(0));
  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(1));
  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
  EXPECT_EQ(1, tab_strip_model->IndexOfFirstNonMiniTab());
}

TEST_F(ProfileResetterTest, ResetShortcuts) {
  ShortcutHandler shortcut;
  ShortcutCommand command_line = shortcut.CreateWithArguments(
      base::ASCIIToUTF16("chrome.lnk"),
      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
      "--profile-directory=Default foo.com"));

  ResetAndWait(ProfileResetter::SHORTCUTS);

  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
      "--profile-directory=Default"));
}

TEST_F(ProfileResetterTest, ResetFewFlags) {
  // mock_object_ is a StrictMock, so we verify that it is called only once.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
               ProfileResetter::HOMEPAGE |
               ProfileResetter::CONTENT_SETTINGS);
}

// Tries to load unavailable config file.
TEST_F(ConfigParserTest, NoConnectivity) {
  const GURL url("http://test");
  factory().SetFakeResponse(url, "", net::HTTP_INTERNAL_SERVER_ERROR,
                            net::URLRequestStatus::FAILED);

  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
  EXPECT_FALSE(fetcher->GetSettings());
}

// Tries to load available config file.
TEST_F(ConfigParserTest, ParseConfig) {
  const GURL url("http://test");
  std::string xml_config(kXmlConfig);
  ReplaceString(&xml_config, "placeholder_for_data", kDistributionConfig);
  ReplaceString(&xml_config,
                "placeholder_for_id",
                "abbaabbaabbaabbaabbaabbaabbaabba");
  factory().SetFakeResponse(url, xml_config, net::HTTP_OK,
                            net::URLRequestStatus::SUCCESS);

  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
  scoped_ptr<BrandcodedDefaultSettings> settings = fetcher->GetSettings();
  ASSERT_TRUE(settings);

  std::vector<std::string> extension_ids;
  EXPECT_TRUE(settings->GetExtensions(&extension_ids));
  EXPECT_EQ(1u, extension_ids.size());
  EXPECT_EQ("abbaabbaabbaabbaabbaabbaabbaabba", extension_ids[0]);

  std::string homepage;
  EXPECT_TRUE(settings->GetHomepage(&homepage));
  EXPECT_EQ("http://www.foo.com", homepage);

  scoped_ptr<base::ListValue> startup_list(
      settings->GetUrlsToRestoreOnStartup());
  EXPECT_TRUE(startup_list);
  std::vector<std::string> startup_pages;
  for (base::ListValue::iterator i = startup_list->begin();
       i != startup_list->end(); ++i) {
    std::string url;
    EXPECT_TRUE((*i)->GetAsString(&url));
    startup_pages.push_back(url);
  }
  ASSERT_EQ(2u, startup_pages.size());
  EXPECT_EQ("http://goo.gl", startup_pages[0]);
  EXPECT_EQ("http://foo.de", startup_pages[1]);
}

TEST_F(ProfileResetterTest, CheckSnapshots) {
  ResettableSettingsSnapshot empty_snap(profile());
  EXPECT_EQ(0, empty_snap.FindDifferentFields(empty_snap));

  scoped_refptr<Extension> ext = CreateExtension(
      base::ASCIIToUTF16("example"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  ASSERT_TRUE(ext);
  service_->AddExtension(ext.get());

  std::string master_prefs(kDistributionConfig);
  std::string ext_id = ext->id();
  ReplaceString(&master_prefs, "placeholder_for_id", ext_id);

  // Reset to non organic defaults.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
               ProfileResetter::HOMEPAGE |
               ProfileResetter::STARTUP_PAGES,
               master_prefs);
  ShortcutHandler shortcut_hijacked;
  ShortcutCommand command_line = shortcut_hijacked.CreateWithArguments(
      base::ASCIIToUTF16("chrome1.lnk"),
      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
  shortcut_hijacked.CheckShortcutHasArguments(
      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
  ShortcutHandler shortcut_ok;
  shortcut_ok.CreateWithArguments(
      base::ASCIIToUTF16("chrome2.lnk"),
      base::ASCIIToUTF16("--profile-directory=Default1"));

  ResettableSettingsSnapshot nonorganic_snap(profile());
  nonorganic_snap.RequestShortcuts(base::Closure());
  // Let it enumerate shortcuts on the FILE thread.
  base::MessageLoop::current()->RunUntilIdle();
  int diff_fields = ResettableSettingsSnapshot::ALL_FIELDS;
  if (!ShortcutHandler::IsSupported())
    diff_fields &= ~ResettableSettingsSnapshot::SHORTCUTS;
  EXPECT_EQ(diff_fields,
            empty_snap.FindDifferentFields(nonorganic_snap));
  empty_snap.Subtract(nonorganic_snap);
  EXPECT_TRUE(empty_snap.startup_urls().empty());
  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(),
            empty_snap.startup_type());
  EXPECT_TRUE(empty_snap.homepage().empty());
  EXPECT_TRUE(empty_snap.homepage_is_ntp());
  EXPECT_NE(std::string::npos, empty_snap.dse_url().find("{google:baseURL}"));
  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(),
            empty_snap.enabled_extensions());
  EXPECT_EQ(std::vector<ShortcutCommand>(), empty_snap.shortcuts());

  // Reset to organic defaults.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
               ProfileResetter::HOMEPAGE |
               ProfileResetter::STARTUP_PAGES |
               ProfileResetter::EXTENSIONS |
               ProfileResetter::SHORTCUTS);

  ResettableSettingsSnapshot organic_snap(profile());
  organic_snap.RequestShortcuts(base::Closure());
  // Let it enumerate shortcuts on the FILE thread.
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_EQ(diff_fields, nonorganic_snap.FindDifferentFields(organic_snap));
  nonorganic_snap.Subtract(organic_snap);
  const GURL urls[] = {GURL("http://foo.de"), GURL("http://goo.gl")};
  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)),
            nonorganic_snap.startup_urls());
  EXPECT_EQ(SessionStartupPref::URLS, nonorganic_snap.startup_type());
  EXPECT_EQ("http://www.foo.com", nonorganic_snap.homepage());
  EXPECT_FALSE(nonorganic_snap.homepage_is_ntp());
  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", nonorganic_snap.dse_url());
  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(
      1, std::make_pair(ext_id, "example")),
      nonorganic_snap.enabled_extensions());
  if (ShortcutHandler::IsSupported()) {
    std::vector<ShortcutCommand> shortcuts = nonorganic_snap.shortcuts();
    ASSERT_EQ(1u, shortcuts.size());
    EXPECT_EQ(command_line.first.value(), shortcuts[0].first.value());
    EXPECT_EQ(command_line.second, shortcuts[0].second);
  }
}

TEST_F(ProfileResetterTest, FeedbackSerializationTest) {
  // Reset to non organic defaults.
  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
               ProfileResetter::HOMEPAGE |
               ProfileResetter::STARTUP_PAGES,
               kDistributionConfig);

  scoped_refptr<Extension> ext = CreateExtension(
      base::ASCIIToUTF16("example"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  ASSERT_TRUE(ext);
  service_->AddExtension(ext.get());

  ShortcutHandler shortcut;
  ShortcutCommand command_line = shortcut.CreateWithArguments(
      base::ASCIIToUTF16("chrome.lnk"),
      base::ASCIIToUTF16("--profile-directory=Default foo.com"));

  ResettableSettingsSnapshot nonorganic_snap(profile());
  nonorganic_snap.RequestShortcuts(base::Closure());
  // Let it enumerate shortcuts on the FILE thread.
  base::MessageLoop::current()->RunUntilIdle();

  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
                 expand_this_test);
  for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS;
       ++field_mask) {
    std::string report = SerializeSettingsReport(nonorganic_snap, field_mask);
    JSONStringValueSerializer json(report);
    std::string error;
    scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
    ASSERT_TRUE(root) << error;
    ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY)) << error;

    base::DictionaryValue* dict =
        static_cast<base::DictionaryValue*>(root.get());

    base::ListValue* startup_urls = NULL;
    int startup_type = 0;
    std::string homepage;
    bool homepage_is_ntp = true;
    std::string default_search_engine;
    base::ListValue* extensions = NULL;
    base::ListValue* shortcuts = NULL;

    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
              dict->GetList("startup_urls", &startup_urls));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
              dict->GetInteger("startup_type", &startup_type));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
              dict->GetString("homepage", &homepage));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
              dict->GetBoolean("homepage_is_ntp", &homepage_is_ntp));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::DSE_URL),
              dict->GetString("default_search_engine", &default_search_engine));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::EXTENSIONS),
              dict->GetList("enabled_extensions", &extensions));
    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::SHORTCUTS),
              dict->GetList("shortcuts", &shortcuts));
  }
}

struct FeedbackCapture {
  void SetFeedback(Profile* profile,
                   const ResettableSettingsSnapshot& snapshot) {
    list_ = GetReadableFeedbackForSnapshot(profile, snapshot).Pass();
    OnUpdatedList();
  }

  void Fail() {
    ADD_FAILURE() << "This method shouldn't be called.";
  }

  MOCK_METHOD0(OnUpdatedList, void(void));

  scoped_ptr<base::ListValue> list_;
};

// Make sure GetReadableFeedback handles non-ascii letters.
TEST_F(ProfileResetterTest, GetReadableFeedback) {
  scoped_refptr<Extension> ext = CreateExtension(
      base::WideToUTF16(L"Tiësto"),
      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
      Manifest::INVALID_LOCATION,
      extensions::Manifest::TYPE_EXTENSION,
      false);
  ASSERT_TRUE(ext);
  service_->AddExtension(ext.get());

  PrefService* prefs = profile()->GetPrefs();
  DCHECK(prefs);
  // The URL is "http://россия.рф".
  std::wstring url(L"http://"
    L"\u0440\u043e\u0441\u0441\u0438\u044f.\u0440\u0444");
  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
  prefs->SetString(prefs::kHomePage, base::WideToUTF8(url));

  SessionStartupPref startup_pref(SessionStartupPref::URLS);
  startup_pref.urls.push_back(GURL(base::WideToUTF8(url)));
  SessionStartupPref::SetStartupPref(prefs, startup_pref);

  ShortcutHandler shortcut;
  ShortcutCommand command_line = shortcut.CreateWithArguments(
      base::ASCIIToUTF16("chrome.lnk"),
      base::ASCIIToUTF16("--profile-directory=Default foo.com"));

  FeedbackCapture capture;
  EXPECT_CALL(capture, OnUpdatedList());
  ResettableSettingsSnapshot snapshot(profile());
  snapshot.RequestShortcuts(base::Bind(&FeedbackCapture::SetFeedback,
                                       base::Unretained(&capture),
                                       profile(),
                                       base::ConstRef(snapshot)));
  // Let it enumerate shortcuts on the FILE thread.
  base::MessageLoop::current()->RunUntilIdle();
  ::testing::Mock::VerifyAndClearExpectations(&capture);
  // The homepage and the startup page are in punycode. They are unreadable.
  // Trying to find the extension name.
  scoped_ptr<base::ListValue> list = capture.list_.Pass();
  ASSERT_TRUE(list);
  bool checked_extensions = false;
  bool checked_shortcuts = false;
  for (size_t i = 0; i < list->GetSize(); ++i) {
    base::DictionaryValue* dict = NULL;
    ASSERT_TRUE(list->GetDictionary(i, &dict));
    std::string value;
    ASSERT_TRUE(dict->GetString("key", &value));
    if (value == "Extensions") {
      base::string16 extensions;
      EXPECT_TRUE(dict->GetString("value", &extensions));
      EXPECT_EQ(base::WideToUTF16(L"Tiësto"), extensions);
      checked_extensions = true;
    } else if (value == "Shortcut targets") {
      base::string16 targets;
      EXPECT_TRUE(dict->GetString("value", &targets));
      EXPECT_NE(base::string16::npos,
                targets.find(base::ASCIIToUTF16("foo.com"))) << targets;
      checked_shortcuts = true;
    }
  }
  EXPECT_TRUE(checked_extensions);
  EXPECT_EQ(ShortcutHandler::IsSupported(), checked_shortcuts);
}

TEST_F(ProfileResetterTest, DestroySnapshotFast) {
  FeedbackCapture capture;
  scoped_ptr<ResettableSettingsSnapshot> deleted_snapshot(
      new ResettableSettingsSnapshot(profile()));
  deleted_snapshot->RequestShortcuts(base::Bind(&FeedbackCapture::Fail,
                                                base::Unretained(&capture)));
  deleted_snapshot.reset();
  // Running remaining tasks shouldn't trigger the callback to be called as
  // |deleted_snapshot| was deleted before it could run.
  base::MessageLoop::current()->RunUntilIdle();
}

}  // namespace

/* [<][>][^][v][top][bottom][index][help] */