// 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.

// Unit tests for helper functions for the Chrome Extensions Proxy Settings API.

#include "base/scoped_ptr.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_proxy_api_constants.h"
#include "chrome/browser/extensions/extension_proxy_api_helpers.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/browser/prefs/proxy_prefs.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace keys = extension_proxy_api_constants;

namespace {

const char kSamplePacScript[] = "test";
const char kSamplePacScriptAsDataUrl[] =
    "data:application/x-ns-proxy-autoconfig;base64,dGVzdA==";
const char kSamplePacScriptUrl[] = "http://wpad/wpad.dat";

// Helper function to create a ProxyServer dictionary as defined in the
// extension API.
DictionaryValue* CreateTestProxyServerDict(const std::string& host) {
  DictionaryValue* dict = new DictionaryValue;
  dict->SetString(keys::kProxyConfigRuleHost, host);
  return dict;
}

// Helper function to create a ProxyServer dictionary as defined in the
// extension API.
DictionaryValue* CreateTestProxyServerDict(const std::string& schema,
                                           const std::string& host,
                                           int port) {
  DictionaryValue* dict = new DictionaryValue;
  dict->SetString(keys::kProxyConfigRuleScheme, schema);
  dict->SetString(keys::kProxyConfigRuleHost, host);
  dict->SetInteger(keys::kProxyConfigRulePort, port);
  return dict;
}

}  // namespace

namespace extension_proxy_api_helpers {

TEST(ExtensionProxyApiHelpers, CreateDataURLFromPACScript) {
  std::string out;
  ASSERT_TRUE(CreateDataURLFromPACScript(kSamplePacScript, &out));
  EXPECT_EQ(kSamplePacScriptAsDataUrl, out);
}

TEST(ExtensionProxyApiHelpers, CreatePACScriptFromDataURL) {
  std::string out;
  ASSERT_TRUE(CreatePACScriptFromDataURL(kSamplePacScriptAsDataUrl, &out));
  EXPECT_EQ(kSamplePacScript, out);

  EXPECT_FALSE(CreatePACScriptFromDataURL("http://www.google.com", &out));
}

TEST(ExtensionProxyApiHelpers, GetProxyModeFromExtensionPref) {
  DictionaryValue proxy_config;
  ProxyPrefs::ProxyMode mode;
  std::string error;

  // Test positive case.
  proxy_config.SetString(
      keys::kProxyConfigMode,
      ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_DIRECT));
  ASSERT_TRUE(GetProxyModeFromExtensionPref(&proxy_config, &mode, &error));
  EXPECT_EQ(ProxyPrefs::MODE_DIRECT, mode);
  EXPECT_EQ(std::string(), error);

  // Test negative case.
  proxy_config.SetString(keys::kProxyConfigMode, "foobar");
  EXPECT_FALSE(GetProxyModeFromExtensionPref(&proxy_config, &mode, &error));

  // Do not test |error|, as an invalid enumeration value is considered an
  // internal error. It should be filtered by the extensions API.
}

TEST(ExtensionProxyApiHelpers, GetPacUrlFromExtensionPref) {
  std::string out;
  std::string error;

  DictionaryValue proxy_config;
  proxy_config.SetString(
      keys::kProxyConfigMode,
      ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT));

  // Currently we are still missing a PAC script entry.
  // This is silently ignored.
  ASSERT_TRUE(GetPacUrlFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(std::string(), out);
  EXPECT_EQ(std::string(), error);

  // Set up a pac script.
  DictionaryValue* pacScriptDict = new DictionaryValue;
  pacScriptDict->SetString(keys::kProxyConfigPacScriptUrl, kSamplePacScriptUrl);
  proxy_config.Set(keys::kProxyConfigPacScript, pacScriptDict);

  ASSERT_TRUE(GetPacUrlFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(kSamplePacScriptUrl, out);
  EXPECT_EQ(std::string(), error);
}

TEST(ExtensionProxyApiHelpers, GetPacDataFromExtensionPref) {
  std::string out;
  std::string error;

  DictionaryValue proxy_config;
  proxy_config.SetString(
      keys::kProxyConfigMode,
      ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT));

  // Currently we are still missing a PAC data entry. This is silently ignored.
  ASSERT_TRUE(GetPacDataFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(std::string(), out);
  EXPECT_EQ(std::string(), error);

  // Set up a PAC script.
  DictionaryValue* pacScriptDict = new DictionaryValue;
  pacScriptDict->SetString(keys::kProxyConfigPacScriptData, kSamplePacScript);
  proxy_config.Set(keys::kProxyConfigPacScript, pacScriptDict);

  ASSERT_TRUE(GetPacDataFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(kSamplePacScript, out);
  EXPECT_EQ(std::string(), error);
}

TEST(ExtensionProxyApiHelpers, GetProxyRulesStringFromExtensionPref) {
  std::string out;
  std::string error;

  DictionaryValue proxy_config;
  proxy_config.SetString(
      keys::kProxyConfigMode,
      ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS));

  // Currently we are still missing a proxy config entry.
  // This is silently ignored.
  ASSERT_TRUE(
      GetProxyRulesStringFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(std::string(), out);
  EXPECT_EQ(std::string(), error);

  DictionaryValue* proxy_rules = new DictionaryValue;
  proxy_rules->Set(keys::field_name[1], CreateTestProxyServerDict("proxy1"));
  proxy_rules->Set(keys::field_name[2], CreateTestProxyServerDict("proxy2"));
  proxy_config.Set(keys::kProxyConfigRules, proxy_rules);

  ASSERT_TRUE(
      GetProxyRulesStringFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ("http=proxy1:80;https=proxy2:80", out);
  EXPECT_EQ(std::string(), error);
}

TEST(ExtensionProxyApiHelpers, GetBypassListFromExtensionPref) {
  std::string out;
  std::string error;

  DictionaryValue proxy_config;
  proxy_config.SetString(
      keys::kProxyConfigMode,
      ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS));

  // Currently we are still missing a proxy config entry.
  // This is silently ignored.
  ASSERT_TRUE(
      GetBypassListFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ(std::string(), out);
  EXPECT_EQ(std::string(), error);

  ListValue* bypass_list = new ListValue;
  bypass_list->Append(Value::CreateStringValue("host1"));
  bypass_list->Append(Value::CreateStringValue("host2"));
  DictionaryValue* proxy_rules = new DictionaryValue;
  proxy_rules->Set(keys::kProxyConfigBypassList, bypass_list);
  proxy_config.Set(keys::kProxyConfigRules, proxy_rules);

  ASSERT_TRUE(
      GetBypassListFromExtensionPref(&proxy_config, &out, &error));
  EXPECT_EQ("host1,host2", out);
  EXPECT_EQ(std::string(), error);
}

TEST(ExtensionProxyApiHelpers, CreateProxyConfigDict) {
  std::string error;
  scoped_ptr<DictionaryValue> exp_direct(ProxyConfigDictionary::CreateDirect());
  scoped_ptr<DictionaryValue> out_direct(
      CreateProxyConfigDict(ProxyPrefs::MODE_DIRECT, "", "", "", "", &error));
  EXPECT_TRUE(Value::Equals(exp_direct.get(), out_direct.get()));

  scoped_ptr<DictionaryValue> exp_auto(
      ProxyConfigDictionary::CreateAutoDetect());
  scoped_ptr<DictionaryValue> out_auto(
      CreateProxyConfigDict(ProxyPrefs::MODE_AUTO_DETECT, "", "", "", "",
                            &error));
  EXPECT_TRUE(Value::Equals(exp_auto.get(), out_auto.get()));

  scoped_ptr<DictionaryValue> exp_pac_url(
      ProxyConfigDictionary::CreatePacScript(kSamplePacScriptUrl));
  scoped_ptr<DictionaryValue> out_pac_url(
        CreateProxyConfigDict(ProxyPrefs::MODE_PAC_SCRIPT, kSamplePacScriptUrl,
                              "", "", "", &error));
  EXPECT_TRUE(Value::Equals(exp_pac_url.get(), out_pac_url.get()));

  scoped_ptr<DictionaryValue> exp_pac_data(
      ProxyConfigDictionary::CreatePacScript(kSamplePacScriptAsDataUrl));
  scoped_ptr<DictionaryValue> out_pac_data(
          CreateProxyConfigDict(ProxyPrefs::MODE_PAC_SCRIPT, "",
                                kSamplePacScript, "", "", &error));
  EXPECT_TRUE(Value::Equals(exp_pac_data.get(), out_pac_data.get()));

  scoped_ptr<DictionaryValue> exp_fixed(
      ProxyConfigDictionary::CreateFixedServers("foo:80", "localhost"));
  scoped_ptr<DictionaryValue> out_fixed(
          CreateProxyConfigDict(ProxyPrefs::MODE_FIXED_SERVERS, "", "",
                                "foo:80", "localhost", &error));
  EXPECT_TRUE(Value::Equals(exp_fixed.get(), out_fixed.get()));

  scoped_ptr<DictionaryValue> exp_system(ProxyConfigDictionary::CreateSystem());
  scoped_ptr<DictionaryValue> out_system(
      CreateProxyConfigDict(ProxyPrefs::MODE_SYSTEM, "", "", "", "", &error));
  EXPECT_TRUE(Value::Equals(exp_system.get(), out_system.get()));

  // Neither of them should have set an error.
  EXPECT_EQ(std::string(), error);
}

TEST(ExtensionProxyApiHelpers, GetProxyServer) {
  DictionaryValue proxy_server_dict;
  net::ProxyServer created;
  std::string error;

  // Test simplest case, no schema nor port specified --> defaults are used.
  proxy_server_dict.SetString(keys::kProxyConfigRuleHost, "proxy_server");
  ASSERT_TRUE(
      GetProxyServer(&proxy_server_dict, net::ProxyServer::SCHEME_HTTP,
                     &created, &error));
  EXPECT_EQ("PROXY proxy_server:80", created.ToPacString());

  // Test complete case.
  proxy_server_dict.SetString(keys::kProxyConfigRuleScheme, "socks4");
  proxy_server_dict.SetInteger(keys::kProxyConfigRulePort, 1234);
  ASSERT_TRUE(
        GetProxyServer(&proxy_server_dict, net::ProxyServer::SCHEME_HTTP,
                       &created, &error));
  EXPECT_EQ("SOCKS proxy_server:1234", created.ToPacString());
}

TEST(ExtensionProxyApiHelpers, JoinUrlList) {
  ListValue list;
  list.Append(Value::CreateStringValue("s1"));
  list.Append(Value::CreateStringValue("s2"));
  list.Append(Value::CreateStringValue("s3"));

  std::string out;
  std::string error;
  ASSERT_TRUE(JoinUrlList(&list, ";", &out, &error));
  EXPECT_EQ("s1;s2;s3", out);
}

// This tests CreateProxyServerDict as well.
TEST(ExtensionProxyApiHelpers, CreateProxyRulesDict) {
  scoped_ptr<DictionaryValue> browser_pref(
      ProxyConfigDictionary::CreateFixedServers(
          "http=proxy1:80;https=proxy2:80;ftp=proxy3:80;socks=proxy4:80",
          "localhost"));
  ProxyConfigDictionary config(browser_pref.get());
  scoped_ptr<DictionaryValue> extension_pref(CreateProxyRulesDict(config));
  ASSERT_TRUE(extension_pref.get());

  scoped_ptr<DictionaryValue> expected(new DictionaryValue);
  expected->Set("proxyForHttp",
                CreateTestProxyServerDict("http", "proxy1", 80));
  expected->Set("proxyForHttps",
                CreateTestProxyServerDict("http", "proxy2", 80));
  expected->Set("proxyForFtp",
                CreateTestProxyServerDict("http", "proxy3", 80));
  expected->Set("fallbackProxy",
                CreateTestProxyServerDict("socks4", "proxy4", 80));
  ListValue* bypass_list = new ListValue;
  bypass_list->Append(Value::CreateStringValue("localhost"));
  expected->Set(keys::kProxyConfigBypassList, bypass_list);

  EXPECT_TRUE(Value::Equals(expected.get(), extension_pref.get()));
}

// Test if a PAC script URL is specified.
TEST(ExtensionProxyApiHelpers, CreatePacScriptDictWithUrl) {
  scoped_ptr<DictionaryValue> browser_pref(
      ProxyConfigDictionary::CreatePacScript(kSamplePacScriptUrl));
  ProxyConfigDictionary config(browser_pref.get());
  scoped_ptr<DictionaryValue> extension_pref(CreatePacScriptDict(config));
  ASSERT_TRUE(extension_pref.get());

  scoped_ptr<DictionaryValue> expected(new DictionaryValue);
  expected->SetString(keys::kProxyConfigPacScriptUrl, kSamplePacScriptUrl);

  EXPECT_TRUE(Value::Equals(expected.get(), extension_pref.get()));
}

// Test if a PAC script is encoded in a data URL.
TEST(ExtensionProxyApiHelpers, CreatePacScriptDictWidthData) {
  scoped_ptr<DictionaryValue> browser_pref(
      ProxyConfigDictionary::CreatePacScript(kSamplePacScriptAsDataUrl));
  ProxyConfigDictionary config(browser_pref.get());
  scoped_ptr<DictionaryValue> extension_pref(CreatePacScriptDict(config));
  ASSERT_TRUE(extension_pref.get());

  scoped_ptr<DictionaryValue> expected(new DictionaryValue);
  expected->SetString(keys::kProxyConfigPacScriptData, kSamplePacScript);

  EXPECT_TRUE(Value::Equals(expected.get(), extension_pref.get()));
}

TEST(ExtensionProxyApiHelpers, TokenizeToStringList) {
  ListValue expected;
  expected.Append(Value::CreateStringValue("s1"));
  expected.Append(Value::CreateStringValue("s2"));
  expected.Append(Value::CreateStringValue("s3"));

  scoped_ptr<ListValue> out(TokenizeToStringList("s1;s2;s3", ";"));
  EXPECT_TRUE(Value::Equals(&expected, out.get()));
}

}  // namespace extension_proxy_api_helpers