// Copyright (c) 2011 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 <string>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/extensions/external_extension_provider_interface.h"
#include "chrome/browser/extensions/external_extension_provider_impl.h"
#include "chrome/browser/extensions/external_policy_extension_loader.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_pref_service.h"
#include "chrome/test/testing_profile.h"
#include "content/browser/browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"

class ExternalPolicyExtensionProviderTest : public testing::Test {
 public:
  ExternalPolicyExtensionProviderTest()
      : loop_(MessageLoop::TYPE_IO),
        ui_thread_(BrowserThread::UI, &loop_) {
  }

  virtual ~ExternalPolicyExtensionProviderTest() {}

 private:
  // We need these to satisfy BrowserThread::CurrentlyOn(BrowserThread::UI)
  // checks in ExternalExtensionProviderImpl.
  MessageLoop loop_;
  BrowserThread ui_thread_;
};

class MockExternalPolicyExtensionProviderVisitor
    : public ExternalExtensionProviderInterface::VisitorInterface {
 public:
  MockExternalPolicyExtensionProviderVisitor() {
  }

  // Initialize a provider with |policy_forcelist|, and check that it parses
  // exactly those extensions, that are specified in |policy_validlist|.
  void Visit(ListValue* policy_forcelist,
             ListValue* policy_validlist,
             const std::set<std::string>& ignore_list) {
    profile_.reset(new TestingProfile);
    profile_->GetTestingPrefService()->SetManagedPref(
        prefs::kExtensionInstallForceList,
        policy_forcelist->DeepCopy());
    provider_.reset(new ExternalExtensionProviderImpl(
        this,
        new ExternalPolicyExtensionLoader(profile_.get()),
        Extension::INVALID,
        Extension::EXTERNAL_POLICY_DOWNLOAD));

    // Extensions will be removed from this list as they visited,
    // so it should be emptied by the end.
    remaining_extensions = policy_validlist;
    provider_->VisitRegisteredExtension();
    EXPECT_EQ(0u, remaining_extensions->GetSize());
  }

  virtual void OnExternalExtensionFileFound(const std::string& id,
                                            const Version* version,
                                            const FilePath& path,
                                            Extension::Location unused) {
    ADD_FAILURE() << "There should be no external extensions from files.";
  }

  virtual void OnExternalExtensionUpdateUrlFound(
      const std::string& id, const GURL& update_url,
      Extension::Location location) {
    // Extension has the correct location.
    EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location);

    // Provider returns the correct location when asked.
    Extension::Location location1;
    scoped_ptr<Version> version1;
    provider_->GetExtensionDetails(id, &location1, &version1);
    EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location1);
    EXPECT_FALSE(version1.get());

    // Remove the extension from our list.
    StringValue ext_str(id + ";" + update_url.spec());
    EXPECT_NE(-1, remaining_extensions->Remove(ext_str));
  }

  virtual void OnExternalProviderReady() {
    EXPECT_TRUE(provider_->IsReady());
  }

 private:
  ListValue* remaining_extensions;

  scoped_ptr<TestingProfile> profile_;

  scoped_ptr<ExternalExtensionProviderImpl> provider_;

  DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyExtensionProviderVisitor);
};

TEST_F(ExternalPolicyExtensionProviderTest, PolicyIsParsed) {
  ListValue forced_extensions;
  forced_extensions.Append(Value::CreateStringValue(
      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http://www.example.com/crx?a=5;b=6"));
  forced_extensions.Append(Value::CreateStringValue(
      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;"
      "https://clients2.google.com/service/update2/crx"));

  MockExternalPolicyExtensionProviderVisitor mv;
  std::set<std::string> empty;
  mv.Visit(&forced_extensions, &forced_extensions, empty);
}

TEST_F(ExternalPolicyExtensionProviderTest, InvalidPolicyIsNotParsed) {
  ListValue forced_extensions, valid_extensions;
  StringValue valid(
      "cccccccccccccccccccccccccccccccc;http://www.example.com/crx");
  valid_extensions.Append(valid.DeepCopy());
  forced_extensions.Append(valid.DeepCopy());
  // Add invalid strings:
  forced_extensions.Append(Value::CreateStringValue(""));
  forced_extensions.Append(Value::CreateStringValue(";"));
  forced_extensions.Append(Value::CreateStringValue(";;"));
  forced_extensions.Append(Value::CreateStringValue(
      ";http://www.example.com/crx"));
  forced_extensions.Append(Value::CreateStringValue(
      ";aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
  forced_extensions.Append(Value::CreateStringValue(
      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"));
  forced_extensions.Append(Value::CreateStringValue(
      "http://www.example.com/crx;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
  forced_extensions.Append(Value::CreateStringValue(
      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http#//www.example.com/crx"));
  forced_extensions.Append(Value::CreateStringValue(
      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
  forced_extensions.Append(Value::CreateStringValue(
      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahttp#//www.example.com/crx"));

  MockExternalPolicyExtensionProviderVisitor mv;
  std::set<std::string> empty;
  mv.Visit(&forced_extensions, &valid_extensions, empty);
}