// Copyright (c) 2012 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 <algorithm>
#include <cstdlib>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/external_data_manager.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_details.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/schema.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

using testing::Return;
using testing::_;

namespace policy {

namespace {

const char kMainSettingsPage[] = "chrome://settings-frame";

const char kCrosSettingsPrefix[] = "cros.";

std::string GetPolicyName(const std::string& policy_name_decorated) {
  const size_t offset = policy_name_decorated.find('.');
  if (offset != std::string::npos)
    return policy_name_decorated.substr(0, offset);
  return policy_name_decorated;
}

// Contains the details of a single test case verifying that the controlled
// setting indicators for a pref affected by a policy work correctly. This is
// part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
class IndicatorTestCase {
 public:
  IndicatorTestCase(const base::DictionaryValue& policy,
                    const std::string& value,
                    bool readonly)
      : policy_(policy.DeepCopy()), value_(value), readonly_(readonly) {}
  ~IndicatorTestCase() {}

  const base::DictionaryValue& policy() const { return *policy_; }

  const std::string& value() const { return value_; }

  bool readonly() const { return readonly_; }

 private:
  scoped_ptr<base::DictionaryValue> policy_;
  std::string value_;
  bool readonly_;

  DISALLOW_COPY_AND_ASSIGN(IndicatorTestCase);
};

// Contains the testing details for a single pref affected by a policy. This is
// part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
class PrefMapping {
 public:
  PrefMapping(const std::string& pref,
              bool is_local_state,
              bool check_for_mandatory,
              bool check_for_recommended,
              const std::string& indicator_test_setup_js,
              const std::string& indicator_selector)
      : pref_(pref),
        is_local_state_(is_local_state),
        check_for_mandatory_(check_for_mandatory),
        check_for_recommended_(check_for_recommended),
        indicator_test_setup_js_(indicator_test_setup_js),
        indicator_selector_(indicator_selector) {}
  ~PrefMapping() {}

  const std::string& pref() const { return pref_; }

  bool is_local_state() const { return is_local_state_; }

  bool check_for_mandatory() const { return check_for_mandatory_; }

  bool check_for_recommended() const { return check_for_recommended_; }

  const std::string& indicator_test_setup_js() const {
    return indicator_test_setup_js_;
  }

  const std::string& indicator_selector() const {
    return indicator_selector_;
  }

  const ScopedVector<IndicatorTestCase>& indicator_test_cases() const {
    return indicator_test_cases_;
  }
  void AddIndicatorTestCase(IndicatorTestCase* test_case) {
    indicator_test_cases_.push_back(test_case);
  }

 private:
  const std::string pref_;
  const bool is_local_state_;
  const bool check_for_mandatory_;
  const bool check_for_recommended_;
  const std::string indicator_test_setup_js_;
  const std::string indicator_selector_;
  ScopedVector<IndicatorTestCase> indicator_test_cases_;

  DISALLOW_COPY_AND_ASSIGN(PrefMapping);
};

// Contains the testing details for a single policy. This is part of the data
// loaded from chrome/test/data/policy/policy_test_cases.json.
class PolicyTestCase {
 public:
  PolicyTestCase(const std::string& name,
                 bool is_official_only,
                 bool can_be_recommended,
                 const std::string& indicator_selector)
      : name_(name),
        is_official_only_(is_official_only),
        can_be_recommended_(can_be_recommended),
        indicator_selector_(indicator_selector) {}
  ~PolicyTestCase() {}

  const std::string& name() const { return name_; }

  bool is_official_only() const { return is_official_only_; }

  bool can_be_recommended() const { return can_be_recommended_; }

  bool IsOsSupported() const {
#if defined(OS_WIN)
    const std::string os("win");
#elif defined(OS_MACOSX)
    const std::string os("mac");
#elif defined(OS_CHROMEOS)
    const std::string os("chromeos");
#elif defined(OS_LINUX)
    const std::string os("linux");
#else
#error "Unknown platform"
#endif
    return std::find(supported_os_.begin(), supported_os_.end(), os) !=
        supported_os_.end();
  }
  void AddSupportedOs(const std::string& os) { supported_os_.push_back(os); }

  bool IsSupported() const {
#if !defined(OFFICIAL_BUILD)
    if (is_official_only())
      return false;
#endif
    return IsOsSupported();
  }

  const base::DictionaryValue& test_policy() const { return test_policy_; }
  void SetTestPolicy(const base::DictionaryValue& policy) {
    test_policy_.Clear();
    test_policy_.MergeDictionary(&policy);
  }

  const ScopedVector<PrefMapping>& pref_mappings() const {
    return pref_mappings_;
  }
  void AddPrefMapping(PrefMapping* pref_mapping) {
    pref_mappings_.push_back(pref_mapping);
  }

  const std::string& indicator_selector() const { return indicator_selector_; }

 private:
  std::string name_;
  bool is_official_only_;
  bool can_be_recommended_;
  std::vector<std::string> supported_os_;
  base::DictionaryValue test_policy_;
  ScopedVector<PrefMapping> pref_mappings_;
  std::string indicator_selector_;

  DISALLOW_COPY_AND_ASSIGN(PolicyTestCase);
};

// Parses all policy test cases and makes then available in a map.
class PolicyTestCases {
 public:
  typedef std::vector<PolicyTestCase*> PolicyTestCaseVector;
  typedef std::map<std::string, PolicyTestCaseVector> PolicyTestCaseMap;
  typedef PolicyTestCaseMap::const_iterator iterator;

  PolicyTestCases() {
    base::FilePath path = ui_test_utils::GetTestFilePath(
        base::FilePath(FILE_PATH_LITERAL("policy")),
        base::FilePath(FILE_PATH_LITERAL("policy_test_cases.json")));
    std::string json;
    if (!base::ReadFileToString(path, &json)) {
      ADD_FAILURE();
      return;
    }
    int error_code = -1;
    std::string error_string;
    base::DictionaryValue* dict = NULL;
    scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
        json, base::JSON_PARSE_RFC, &error_code, &error_string));
    if (!value.get() || !value->GetAsDictionary(&dict)) {
      ADD_FAILURE() << "Error parsing policy_test_cases.json: " << error_string;
      return;
    }
    Schema chrome_schema = Schema::Wrap(GetChromeSchemaData());
    if (!chrome_schema.valid()) {
      ADD_FAILURE();
      return;
    }
    for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
         it.Advance()) {
      const std::string policy_name = GetPolicyName(it.key());
      if (!chrome_schema.GetKnownProperty(policy_name).valid())
        continue;
      PolicyTestCase* policy_test_case = GetPolicyTestCase(dict, it.key());
      if (policy_test_case)
        policy_test_cases_[policy_name].push_back(policy_test_case);
    }
  }

  ~PolicyTestCases() {
    for (iterator policy = policy_test_cases_.begin();
         policy != policy_test_cases_.end();
         ++policy) {
      for (PolicyTestCaseVector::const_iterator test_case =
               policy->second.begin();
           test_case != policy->second.end();
           ++test_case) {
        delete *test_case;
      }
    }
  }

  const PolicyTestCaseVector* Get(const std::string& name) const {
    const iterator it = policy_test_cases_.find(name);
    return it == end() ? NULL : &it->second;
  }

  const PolicyTestCaseMap& map() const { return policy_test_cases_; }
  iterator begin() const { return policy_test_cases_.begin(); }
  iterator end() const { return policy_test_cases_.end(); }

 private:
  PolicyTestCase* GetPolicyTestCase(const base::DictionaryValue* tests,
                                    const std::string& name) {
    const base::DictionaryValue* policy_test_dict = NULL;
    if (!tests->GetDictionaryWithoutPathExpansion(name, &policy_test_dict))
      return NULL;
    bool is_official_only = false;
    policy_test_dict->GetBoolean("official_only", &is_official_only);
    bool can_be_recommended = false;
    policy_test_dict->GetBoolean("can_be_recommended", &can_be_recommended);
    std::string indicator_selector;
    policy_test_dict->GetString("indicator_selector", &indicator_selector);
    PolicyTestCase* policy_test_case = new PolicyTestCase(name,
                                                          is_official_only,
                                                          can_be_recommended,
                                                          indicator_selector);
    const base::ListValue* os_list = NULL;
    if (policy_test_dict->GetList("os", &os_list)) {
      for (size_t i = 0; i < os_list->GetSize(); ++i) {
        std::string os;
        if (os_list->GetString(i, &os))
          policy_test_case->AddSupportedOs(os);
      }
    }
    const base::DictionaryValue* policy = NULL;
    if (policy_test_dict->GetDictionary("test_policy", &policy))
      policy_test_case->SetTestPolicy(*policy);
    const base::ListValue* pref_mappings = NULL;
    if (policy_test_dict->GetList("pref_mappings", &pref_mappings)) {
      for (size_t i = 0; i < pref_mappings->GetSize(); ++i) {
        const base::DictionaryValue* pref_mapping_dict = NULL;
        std::string pref;
        if (!pref_mappings->GetDictionary(i, &pref_mapping_dict) ||
            !pref_mapping_dict->GetString("pref", &pref)) {
          ADD_FAILURE() << "Malformed pref_mappings entry in "
                        << "policy_test_cases.json.";
          continue;
        }
        bool is_local_state = false;
        pref_mapping_dict->GetBoolean("local_state", &is_local_state);
        bool check_for_mandatory = true;
        pref_mapping_dict->GetBoolean("check_for_mandatory",
                                      &check_for_mandatory);
        bool check_for_recommended = true;
        pref_mapping_dict->GetBoolean("check_for_recommended",
                                      &check_for_recommended);
        std::string indicator_test_setup_js;
        pref_mapping_dict->GetString("indicator_test_setup_js",
                                     &indicator_test_setup_js);
        std::string indicator_selector;
        pref_mapping_dict->GetString("indicator_selector", &indicator_selector);
        PrefMapping* pref_mapping = new PrefMapping(pref,
                                                    is_local_state,
                                                    check_for_mandatory,
                                                    check_for_recommended,
                                                    indicator_test_setup_js,
                                                    indicator_selector);
        const base::ListValue* indicator_tests = NULL;
        if (pref_mapping_dict->GetList("indicator_tests", &indicator_tests)) {
          for (size_t i = 0; i < indicator_tests->GetSize(); ++i) {
            const base::DictionaryValue* indicator_test_dict = NULL;
            const base::DictionaryValue* policy = NULL;
            if (!indicator_tests->GetDictionary(i, &indicator_test_dict) ||
                !indicator_test_dict->GetDictionary("policy", &policy)) {
              ADD_FAILURE() << "Malformed indicator_tests entry in "
                            << "policy_test_cases.json.";
              continue;
            }
            std::string value;
            indicator_test_dict->GetString("value", &value);
            bool readonly = false;
            indicator_test_dict->GetBoolean("readonly", &readonly);
            pref_mapping->AddIndicatorTestCase(
                new IndicatorTestCase(*policy, value, readonly));
          }
        }
        policy_test_case->AddPrefMapping(pref_mapping);
      }
    }
    return policy_test_case;
  }

  PolicyTestCaseMap policy_test_cases_;

  DISALLOW_COPY_AND_ASSIGN(PolicyTestCases);
};

// Returns a pseudo-random integer distributed in [0, range).
int GetRandomNumber(int range) {
  return rand() % range;
}

// Splits all known policies into subsets of the given |chunk_size|. The
// policies are shuffled so that there is no correlation between their initial
// alphabetic ordering and the assignment to chunks. This ensures that the
// expected number of policies with long-running test cases is equal for each
// subset. The shuffle algorithm uses a fixed seed, ensuring that no randomness
// is introduced into the testing process.
std::vector<std::vector<std::string> > SplitPoliciesIntoChunks(int chunk_size) {
  Schema chrome_schema = Schema::Wrap(GetChromeSchemaData());
  if (!chrome_schema.valid())
    ADD_FAILURE();

  std::vector<std::string> policies;
  for (Schema::Iterator it = chrome_schema.GetPropertiesIterator();
       !it.IsAtEnd(); it.Advance()) {
    policies.push_back(it.key());
  }

  // Use a fixed random seed to obtain a reproducible shuffle.
  srand(1);
  std::random_shuffle(policies.begin(), policies.end(), GetRandomNumber);

  std::vector<std::vector<std::string> > chunks;
  std::vector<std::string>::const_iterator it = policies.begin();
  const std::vector<std::string>::const_iterator end = policies.end();
  for ( ; end - it >= chunk_size; it += chunk_size)
    chunks.push_back(std::vector<std::string>(it, it + chunk_size));
  if (it != end)
    chunks.push_back(std::vector<std::string>(it, end));
  return chunks;
}

void VerifyControlledSettingIndicators(Browser* browser,
                                       const std::string& selector,
                                       const std::string& value,
                                       const std::string& controlled_by,
                                       bool readonly) {
  std::stringstream javascript;
  javascript << "var nodes = document.querySelectorAll("
             << "    'span.controlled-setting-indicator"
             <<          selector.c_str() << "');"
             << "var indicators = [];"
             << "for (var i = 0; i < nodes.length; i++) {"
             << "  var node = nodes[i];"
             << "  var indicator = {};"
             << "  indicator.value = node.value || '';"
             << "  indicator.controlledBy = node.controlledBy || '';"
             << "  indicator.readOnly = node.readOnly || false;"
             << "  indicator.visible ="
             << "      window.getComputedStyle(node).display != 'none';"
             << "  indicators.push(indicator)"
             << "}"
             << "domAutomationController.send(JSON.stringify(indicators));";
  content::WebContents* contents =
      browser->tab_strip_model()->GetActiveWebContents();
  std::string json;
  // Retrieve the state of all controlled setting indicators matching the
  // |selector| as JSON.
  ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, javascript.str(),
                                                     &json));
  scoped_ptr<base::Value> value_ptr(base::JSONReader::Read(json));
  const base::ListValue* indicators = NULL;
  ASSERT_TRUE(value_ptr.get());
  ASSERT_TRUE(value_ptr->GetAsList(&indicators));
  // Verify that controlled setting indicators representing |value| are visible
  // and have the correct state while those not representing |value| are
  // invisible.
  if (!controlled_by.empty()) {
    EXPECT_GT(indicators->GetSize(), 0u)
        << "Expected to find at least one controlled setting indicator.";
  }
  bool have_visible_indicators = false;
  for (base::ListValue::const_iterator indicator = indicators->begin();
       indicator != indicators->end(); ++indicator) {
    const base::DictionaryValue* properties = NULL;
    ASSERT_TRUE((*indicator)->GetAsDictionary(&properties));
    std::string indicator_value;
    std::string indicator_controlled_by;
    bool indicator_readonly;
    bool indicator_visible;
    EXPECT_TRUE(properties->GetString("value", &indicator_value));
    EXPECT_TRUE(properties->GetString("controlledBy",
                                      &indicator_controlled_by));
    EXPECT_TRUE(properties->GetBoolean("readOnly", &indicator_readonly));
    EXPECT_TRUE(properties->GetBoolean("visible", &indicator_visible));
    if (!controlled_by.empty() && (indicator_value == value)) {
      EXPECT_EQ(controlled_by, indicator_controlled_by);
      EXPECT_EQ(readonly, indicator_readonly);
      EXPECT_TRUE(indicator_visible);
      have_visible_indicators = true;
    } else {
      EXPECT_FALSE(indicator_visible);
    }
  }
  if (!controlled_by.empty()) {
    EXPECT_TRUE(have_visible_indicators)
        << "Expected to find at least one visible controlled setting "
        << "indicator.";
  }
}

}  // namespace

typedef InProcessBrowserTest PolicyPrefsTestCoverageTest;

IN_PROC_BROWSER_TEST_F(PolicyPrefsTestCoverageTest, AllPoliciesHaveATestCase) {
  // Verifies that all known policies have a test case in the JSON file.
  // This test fails when a policy is added to
  // components/policy/resources/policy_templates.json but a test case is not
  // added to chrome/test/data/policy/policy_test_cases.json.
  Schema chrome_schema = Schema::Wrap(GetChromeSchemaData());
  ASSERT_TRUE(chrome_schema.valid());

  PolicyTestCases policy_test_cases;
  for (Schema::Iterator it = chrome_schema.GetPropertiesIterator();
       !it.IsAtEnd(); it.Advance()) {
    EXPECT_TRUE(ContainsKey(policy_test_cases.map(), it.key()))
        << "Missing policy test case for: " << it.key();
  }
}

// Base class for tests that change policy.
class PolicyPrefsTest : public InProcessBrowserTest {
 protected:
  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    EXPECT_CALL(provider_, IsInitializationComplete(_))
        .WillRepeatedly(Return(true));
    BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
  }

  virtual void SetUpOnMainThread() OVERRIDE {
    ui_test_utils::WaitForTemplateURLServiceToLoad(
        TemplateURLServiceFactory::GetForProfile(browser()->profile()));
  }

  virtual void TearDownOnMainThread() OVERRIDE {
    ClearProviderPolicy();
  }

  void ClearProviderPolicy() {
    provider_.UpdateChromePolicy(PolicyMap());
    base::RunLoop().RunUntilIdle();
  }

  void SetProviderPolicy(const base::DictionaryValue& policies,
                         PolicyLevel level) {
    PolicyMap policy_map;
    for (base::DictionaryValue::Iterator it(policies);
         !it.IsAtEnd(); it.Advance()) {
      const PolicyDetails* policy_details = GetChromePolicyDetails(it.key());
      ASSERT_TRUE(policy_details);
      policy_map.Set(
          it.key(),
          level,
          POLICY_SCOPE_USER,
          it.value().DeepCopy(),
          policy_details->max_external_data_size ?
              new ExternalDataFetcher(base::WeakPtr<ExternalDataManager>(),
                                      it.key()) :
              NULL);
    }
    provider_.UpdateChromePolicy(policy_map);
    base::RunLoop().RunUntilIdle();
  }

  MockConfigurationPolicyProvider provider_;
};

// Verifies that policies make their corresponding preferences become managed,
// and that the user can't override that setting.
IN_PROC_BROWSER_TEST_F(PolicyPrefsTest, PolicyToPrefsMapping) {
  PrefService* local_state = g_browser_process->local_state();
  PrefService* user_prefs = browser()->profile()->GetPrefs();

  const PolicyTestCases test_cases;
  for (PolicyTestCases::iterator policy = test_cases.begin();
       policy != test_cases.end();
       ++policy) {
    for (PolicyTestCases::PolicyTestCaseVector::const_iterator test_case =
             policy->second.begin();
         test_case != policy->second.end();
         ++test_case) {
      const ScopedVector<PrefMapping>& pref_mappings =
          (*test_case)->pref_mappings();
      if (!(*test_case)->IsSupported() || pref_mappings.empty())
        continue;

      LOG(INFO) << "Testing policy: " << policy->first;

      for (ScopedVector<PrefMapping>::const_iterator pref_mapping =
               pref_mappings.begin();
           pref_mapping != pref_mappings.end();
           ++pref_mapping) {
        // Skip Chrome OS preferences that use a different backend and cannot be
        // retrieved through the prefs mechanism.
        if (StartsWithASCII((*pref_mapping)->pref(), kCrosSettingsPrefix, true))
          continue;

        // Skip preferences that should not be checked when the policy is set to
        // a mandatory value.
        if (!(*pref_mapping)->check_for_mandatory())
          continue;

        PrefService* prefs =
            (*pref_mapping)->is_local_state() ? local_state : user_prefs;
        // The preference must have been registered.
        const PrefService::Preference* pref =
            prefs->FindPreference((*pref_mapping)->pref().c_str());
        ASSERT_TRUE(pref);

        // Verify that setting the policy overrides the pref.
        ClearProviderPolicy();
        prefs->ClearPref((*pref_mapping)->pref().c_str());
        EXPECT_TRUE(pref->IsDefaultValue());
        EXPECT_TRUE(pref->IsUserModifiable());
        EXPECT_FALSE(pref->IsUserControlled());
        EXPECT_FALSE(pref->IsManaged());

        SetProviderPolicy((*test_case)->test_policy(), POLICY_LEVEL_MANDATORY);
        EXPECT_FALSE(pref->IsDefaultValue());
        EXPECT_FALSE(pref->IsUserModifiable());
        EXPECT_FALSE(pref->IsUserControlled());
        EXPECT_TRUE(pref->IsManaged());
      }
    }
  }
}

class PolicyPrefIndicatorTest
    : public PolicyPrefsTest,
      public testing::WithParamInterface<std::vector<std::string> > {
};

// Verifies that controlled setting indicators correctly show whether a pref's
// value is recommended or enforced by a corresponding policy.
IN_PROC_BROWSER_TEST_P(PolicyPrefIndicatorTest, CheckPolicyIndicators) {
  const PolicyTestCases test_cases;
  PrefService* local_state = g_browser_process->local_state();
  PrefService* user_prefs = browser()->profile()->GetPrefs();

  ui_test_utils::NavigateToURL(browser(), GURL(kMainSettingsPage));

  for (std::vector<std::string>::const_iterator policy = GetParam().begin();
       policy != GetParam().end();
       ++policy) {
    const std::vector<PolicyTestCase*>* policy_test_cases =
        test_cases.Get(*policy);
    ASSERT_TRUE(policy_test_cases) << "PolicyTestCase not found for "
                                   << *policy;
    for (std::vector<PolicyTestCase*>::const_iterator test_case =
             policy_test_cases->begin();
         test_case != policy_test_cases->end();
         ++test_case) {
      PolicyTestCase* policy_test_case = *test_case;
      if (!policy_test_case->IsSupported())
        continue;
      const ScopedVector<PrefMapping>& pref_mappings =
          policy_test_case->pref_mappings();
      if (policy_test_case->indicator_selector().empty()) {
        bool has_pref_indicator_tests = false;
        for (ScopedVector<PrefMapping>::const_iterator pref_mapping =
                 pref_mappings.begin();
             pref_mapping != pref_mappings.end();
             ++pref_mapping) {
          if (!(*pref_mapping)->indicator_test_cases().empty()) {
            has_pref_indicator_tests = true;
            break;
          }
        }
        if (!has_pref_indicator_tests)
          continue;
      }

      LOG(INFO) << "Testing policy: " << *policy;

      if (!policy_test_case->indicator_selector().empty()) {
        // Check that no controlled setting indicator is visible when no value
        // is set by policy.
        ClearProviderPolicy();
        VerifyControlledSettingIndicators(
            browser(),
            policy_test_case->indicator_selector(),
            std::string(),
            std::string(),
            false);
        // Check that the appropriate controlled setting indicator is shown when
        // a value is enforced by policy.
        SetProviderPolicy(policy_test_case->test_policy(),
                          POLICY_LEVEL_MANDATORY);
        VerifyControlledSettingIndicators(
            browser(),
            policy_test_case->indicator_selector(),
            std::string(),
            "policy",
            false);
      }

      for (ScopedVector<PrefMapping>::const_iterator
               pref_mapping = pref_mappings.begin();
           pref_mapping != pref_mappings.end();
           ++pref_mapping) {
        const ScopedVector<IndicatorTestCase>& indicator_test_cases =
            (*pref_mapping)->indicator_test_cases();
        if (indicator_test_cases.empty())
          continue;

        if (!(*pref_mapping)->indicator_test_setup_js().empty()) {
          ASSERT_TRUE(content::ExecuteScript(
              browser()->tab_strip_model()->GetActiveWebContents(),
              (*pref_mapping)->indicator_test_setup_js()));
        }

        std::string indicator_selector = (*pref_mapping)->indicator_selector();
        if (indicator_selector.empty())
          indicator_selector = "[pref=\"" + (*pref_mapping)->pref() + "\"]";
        for (ScopedVector<IndicatorTestCase>::const_iterator
                 indicator_test_case = indicator_test_cases.begin();
             indicator_test_case != indicator_test_cases.end();
             ++indicator_test_case) {
          // Check that no controlled setting indicator is visible when no value
          // is set by policy.
          ClearProviderPolicy();
          VerifyControlledSettingIndicators(browser(),
                                            indicator_selector,
                                            std::string(),
                                            std::string(),
                                            false);

          if ((*pref_mapping)->check_for_mandatory()) {
            // Check that the appropriate controlled setting indicator is shown
            // when a value is enforced by policy.
            SetProviderPolicy((*indicator_test_case)->policy(),
                              POLICY_LEVEL_MANDATORY);

            VerifyControlledSettingIndicators(
                browser(),
                indicator_selector,
                (*indicator_test_case)->value(),
                "policy",
                (*indicator_test_case)->readonly());
          }

          if (!policy_test_case->can_be_recommended() ||
              !(*pref_mapping)->check_for_recommended()) {
            continue;
          }

          PrefService* prefs =
              (*pref_mapping)->is_local_state() ? local_state : user_prefs;
          // The preference must have been registered.
          const PrefService::Preference* pref =
              prefs->FindPreference((*pref_mapping)->pref().c_str());
          ASSERT_TRUE(pref);

          // Check that the appropriate controlled setting indicator is shown
          // when a value is recommended by policy and the user has not
          // overridden the recommendation.
          SetProviderPolicy((*indicator_test_case)->policy(),
                            POLICY_LEVEL_RECOMMENDED);
          VerifyControlledSettingIndicators(browser(),
                                            indicator_selector,
                                            (*indicator_test_case)->value(),
                                            "recommended",
                                            (*indicator_test_case)->readonly());
          // Check that the appropriate controlled setting indicator is shown
          // when a value is recommended by policy and the user has overridden
          // the recommendation.
          prefs->Set((*pref_mapping)->pref().c_str(), *pref->GetValue());
          VerifyControlledSettingIndicators(browser(),
                                            indicator_selector,
                                            (*indicator_test_case)->value(),
                                            "hasRecommendation",
                                            (*indicator_test_case)->readonly());
          prefs->ClearPref((*pref_mapping)->pref().c_str());
        }
      }
    }
  }
}

INSTANTIATE_TEST_CASE_P(PolicyPrefIndicatorTestInstance,
                        PolicyPrefIndicatorTest,
                        testing::ValuesIn(SplitPoliciesIntoChunks(10)));

}  // namespace policy