aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2013-08-27 10:18:33 -0400
committerJason Monk <jmonk@google.com>2013-08-27 13:27:52 -0400
commitf34af54bcab917eb5943d1918f32d26364f45578 (patch)
tree52084c98b242010fd4f4d9f180c501849cea2af9
parentc34a8c90027b55d8f5b5c8e90592fafb72b32c3a (diff)
downloadchromium-libpac-kitkat-release.tar.gz
This converts the gtest unit tests for PAC from chromium to libpac. Bug: 10504578 Change-Id: If0b93133808c425516a6637c2802cb69dc5e2f43
-rw-r--r--test/Android.mk27
-rw-r--r--test/README6
-rw-r--r--test/js-unittest/binding_from_global.js8
-rw-r--r--test/js-unittest/bindings.js62
-rw-r--r--test/js-unittest/direct.js4
-rw-r--r--test/js-unittest/dns_fail.js27
-rw-r--r--test/js-unittest/ends_with_comment.js8
-rw-r--r--test/js-unittest/ends_with_statement_no_semicolon.js3
-rw-r--r--test/js-unittest/international_domain_names.js16
-rw-r--r--test/js-unittest/missing_close_brace.js6
-rw-r--r--test/js-unittest/no_entrypoint.js2
-rw-r--r--test/js-unittest/pac_library_unittest.js366
-rw-r--r--test/js-unittest/passthrough.js45
-rw-r--r--test/js-unittest/return_empty_string.js4
-rw-r--r--test/js-unittest/return_function.js4
-rw-r--r--test/js-unittest/return_integer.js4
-rw-r--r--test/js-unittest/return_null.js4
-rw-r--r--test/js-unittest/return_object.js4
-rw-r--r--test/js-unittest/return_undefined.js4
-rw-r--r--test/js-unittest/return_unicode.js4
-rw-r--r--test/js-unittest/side_effects.js10
-rw-r--r--test/js-unittest/simple.js21
-rw-r--r--test/js-unittest/unhandled_exception.js5
-rwxr-xr-xtest/jstocstring.pl27
-rw-r--r--test/proxy_resolver_v8_unittest.cc547
-rw-r--r--test/proxy_test_script.h660
26 files changed, 1878 insertions, 0 deletions
diff --git a/test/Android.mk b/test/Android.mk
new file mode 100644
index 0000000..1f04f1f
--- /dev/null
+++ b/test/Android.mk
@@ -0,0 +1,27 @@
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+# Set up the target identity
+LOCAL_MODULE := proxy_resolver_v8_unittest
+
+LOCAL_SRC_FILES := \
+ proxy_resolver_v8_unittest.cc
+
+LOCAL_CFLAGS += \
+ -Wno-endif-labels \
+ -Wno-import \
+ -Wno-format \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../src $(LOCAL_PATH)/ external/v8 external/gtest
+
+LOCAL_SHARED_LIBRARIES := libpac libutils libstlport liblog
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_NATIVE_TEST)
+
+endif
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..19d2ec0
--- /dev/null
+++ b/test/README
@@ -0,0 +1,6 @@
+This directory contains a script that converts the javascript directory
+to a header file containing strings for use with testing.
+
+Do not modify proxy_script_test.h. Instead modify the files contained
+within js-unittest/ and then run the following command.
+ ./jstocstring.pl js-unittest proxy_test_script.h
diff --git a/test/js-unittest/binding_from_global.js b/test/js-unittest/binding_from_global.js
new file mode 100644
index 0000000..91bbcf2
--- /dev/null
+++ b/test/js-unittest/binding_from_global.js
@@ -0,0 +1,8 @@
+// Calls a bindings outside of FindProxyForURL(). This causes the code to
+// get exercised during initialization.
+
+var x = myIpAddress();
+
+function FindProxyForURL(url, host) {
+ return "PROXY " + x + ":80";
+}
diff --git a/test/js-unittest/bindings.js b/test/js-unittest/bindings.js
new file mode 100644
index 0000000..7cf9f26
--- /dev/null
+++ b/test/js-unittest/bindings.js
@@ -0,0 +1,62 @@
+// Try calling the browser-side bound functions with varying (invalid)
+// inputs. There is no notion of "success" for this test, other than
+// verifying the correct C++ bindings were reached with expected values.
+
+function MyObject() {
+ this.x = "3";
+}
+
+MyObject.prototype.toString = function() {
+ throw "exception from calling toString()";
+}
+
+function expectEquals(expectation, actual) {
+ if (!(expectation === actual)) {
+ throw "FAIL: expected: " + expectation + ", actual: " + actual;
+ }
+}
+
+function FindProxyForURL(url, host) {
+ // Call dnsResolve with some wonky arguments.
+ // Those expected to fail (because we have passed a non-string parameter)
+ // will return |null|, whereas those that have called through to the C++
+ // bindings will return '127.0.0.1'.
+ expectEquals(null, dnsResolve());
+ expectEquals(null, dnsResolve(null));
+ expectEquals(null, dnsResolve(undefined));
+ expectEquals('127.0.0.1', dnsResolve(""));
+ expectEquals(null, dnsResolve({foo: 'bar'}));
+ expectEquals(null, dnsResolve(fn));
+ expectEquals(null, dnsResolve(['3']));
+ expectEquals('127.0.0.1', dnsResolve("arg1", "arg2", "arg3", "arg4"));
+
+ // Call alert with some wonky arguments.
+ alert();
+ alert(null);
+ alert(undefined);
+ alert({foo:'bar'});
+
+ // This should throw an exception when we toString() the argument
+ // to alert in the bindings.
+ try {
+ alert(new MyObject());
+ } catch (e) {
+ alert(e);
+ }
+
+ // Call myIpAddress() with wonky arguments
+ myIpAddress(null);
+ myIpAddress(null, null);
+
+ // Call myIpAddressEx() correctly (no arguments).
+ myIpAddressEx();
+
+ // Call dnsResolveEx() (note that isResolvableEx() implicity calls it.)
+ isResolvableEx("is_resolvable");
+ dnsResolveEx("foobar");
+
+ return "DIRECT";
+}
+
+function fn() {}
+
diff --git a/test/js-unittest/direct.js b/test/js-unittest/direct.js
new file mode 100644
index 0000000..43a04da
--- /dev/null
+++ b/test/js-unittest/direct.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return "DIRECT";
+}
+
diff --git a/test/js-unittest/dns_fail.js b/test/js-unittest/dns_fail.js
new file mode 100644
index 0000000..c71bcc3
--- /dev/null
+++ b/test/js-unittest/dns_fail.js
@@ -0,0 +1,27 @@
+// This script should be run in an environment where all DNS resolution are
+// failing. It tests that functions return the expected values.
+//
+// Returns "PROXY success:80" on success.
+function FindProxyForURL(url, host) {
+ try {
+ expectEq("127.0.0.1", myIpAddress());
+ expectEq("", myIpAddressEx());
+
+ expectEq(null, dnsResolve("not-found"));
+ expectEq("", dnsResolveEx("not-found"));
+
+ expectEq(false, isResolvable("not-found"));
+ expectEq(false, isResolvableEx("not-found"));
+
+ return "PROXY success:80";
+ } catch(e) {
+ alert(e);
+ return "PROXY failed:80";
+ }
+}
+
+function expectEq(expected, actual) {
+ if (expected != actual)
+ throw "Expected " + expected + " but was " + actual;
+}
+
diff --git a/test/js-unittest/ends_with_comment.js b/test/js-unittest/ends_with_comment.js
new file mode 100644
index 0000000..bbfef85
--- /dev/null
+++ b/test/js-unittest/ends_with_comment.js
@@ -0,0 +1,8 @@
+function FindProxyForURL(url, host) {
+ return "PROXY success:80";
+}
+
+// We end the script with a comment (and no trailing newline).
+// This used to cause problems, because internally ProxyResolverV8
+// would append some functions to the script; the first line of
+// those extra functions was being considered part of the comment. \ No newline at end of file
diff --git a/test/js-unittest/ends_with_statement_no_semicolon.js b/test/js-unittest/ends_with_statement_no_semicolon.js
new file mode 100644
index 0000000..ea03714
--- /dev/null
+++ b/test/js-unittest/ends_with_statement_no_semicolon.js
@@ -0,0 +1,3 @@
+// Ends with a statement, and no terminal newline.
+function FindProxyForURL(url, host) { return "PROXY success:" + x; }
+x = 3 \ No newline at end of file
diff --git a/test/js-unittest/international_domain_names.js b/test/js-unittest/international_domain_names.js
new file mode 100644
index 0000000..546af13
--- /dev/null
+++ b/test/js-unittest/international_domain_names.js
@@ -0,0 +1,16 @@
+// Try resolving hostnames containing non-ASCII characters.
+
+function FindProxyForURL(url, host) {
+ // This international hostname has a non-ASCII character. It is represented
+ // in punycode as 'xn--bcher-kva.ch'
+ var idn = 'B\u00fccher.ch';
+
+ // We disregard the actual return value -- all we care about is that on
+ // the C++ end the bindings were passed the punycode equivalent of this
+ // unicode hostname.
+ dnsResolve(idn);
+ dnsResolveEx(idn);
+
+ return "DIRECT";
+}
+
diff --git a/test/js-unittest/missing_close_brace.js b/test/js-unittest/missing_close_brace.js
new file mode 100644
index 0000000..8018f8f
--- /dev/null
+++ b/test/js-unittest/missing_close_brace.js
@@ -0,0 +1,6 @@
+// This PAC script is invalid, because there is a missing close brace
+// on the function FindProxyForURL().
+
+function FindProxyForURL(url, host) {
+ return "DIRECT";
+
diff --git a/test/js-unittest/no_entrypoint.js b/test/js-unittest/no_entrypoint.js
new file mode 100644
index 0000000..8993059
--- /dev/null
+++ b/test/js-unittest/no_entrypoint.js
@@ -0,0 +1,2 @@
+var x = "This is an invalid PAC script because it lacks a " +
+ "FindProxyForURL() function";
diff --git a/test/js-unittest/pac_library_unittest.js b/test/js-unittest/pac_library_unittest.js
new file mode 100644
index 0000000..0c0a4a9
--- /dev/null
+++ b/test/js-unittest/pac_library_unittest.js
@@ -0,0 +1,366 @@
+// This should output "PROXY success:80" if all the tests pass.
+// Otherwise it will output "PROXY failure:<num-failures>".
+//
+// This aims to unit-test the PAC library functions, which are
+// exposed in the PAC's execution environment. (Namely, dnsDomainLevels,
+// timeRange, etc.)
+
+function FindProxyForURL(url, host) {
+ var numTestsFailed = 0;
+
+ // Run all the tests
+ for (var test in Tests) {
+ var t = new TestContext(test);
+
+ // Run the test.
+ Tests[test](t);
+
+ if (t.failed()) {
+ numTestsFailed++;
+ }
+ }
+
+ if (numTestsFailed == 0) {
+ return "PROXY success:80";
+ }
+ return "PROXY failure:" + numTestsFailed;
+}
+
+// --------------------------
+// Tests
+// --------------------------
+
+var Tests = {};
+
+Tests.testDnsDomainIs = function(t) {
+ t.expectTrue(dnsDomainIs("google.com", ".com"));
+ t.expectTrue(dnsDomainIs("google.co.uk", ".co.uk"));
+ t.expectFalse(dnsDomainIs("google.com", ".co.uk"));
+ t.expectFalse(dnsDomainIs("www.adobe.com", ".ad"));
+};
+
+Tests.testDnsDomainLevels = function(t) {
+ t.expectEquals(0, dnsDomainLevels("www"));
+ t.expectEquals(2, dnsDomainLevels("www.google.com"));
+ t.expectEquals(3, dnsDomainLevels("192.168.1.1"));
+};
+
+Tests.testIsInNet = function(t) {
+ t.expectTrue(
+ isInNet("192.89.132.25", "192.89.132.25", "255.255.255.255"));
+ t.expectFalse(
+ isInNet("193.89.132.25", "192.89.132.25", "255.255.255.255"));
+
+ t.expectTrue(isInNet("192.89.132.25", "192.89.0.0", "255.255.0.0"));
+ t.expectFalse(isInNet("193.89.132.25", "192.89.0.0", "255.255.0.0"));
+
+ t.expectFalse(
+ isInNet("192.89.132.a", "192.89.0.0", "255.255.0.0"));
+};
+
+Tests.testIsPlainHostName = function(t) {
+ t.expectTrue(isPlainHostName("google"));
+ t.expectFalse(isPlainHostName("google.com"));
+};
+
+Tests.testLocalHostOrDomainIs = function(t) {
+ t.expectTrue(localHostOrDomainIs("www.google.com", "www.google.com"));
+ t.expectTrue(localHostOrDomainIs("www", "www.google.com"));
+ t.expectFalse(localHostOrDomainIs("maps.google.com", "www.google.com"));
+};
+
+Tests.testShExpMatch = function(t) {
+ t.expectTrue(shExpMatch("foo.jpg", "*.jpg"));
+ t.expectTrue(shExpMatch("foo5.jpg", "*o?.jpg"));
+ t.expectFalse(shExpMatch("foo.jpg", ".jpg"));
+ t.expectFalse(shExpMatch("foo.jpg", "foo"));
+};
+
+Tests.testSortIpAddressList = function(t) {
+ t.expectEquals("::1;::2;::3", sortIpAddressList("::2;::3;::1"));
+ t.expectEquals(
+ "2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22",
+ sortIpAddressList("157.59.139.22;" +
+ "2001:4898:28:3:201:2ff:feea:fc14;" +
+ "fe80::5efe:157:9d3b:8b16"));
+
+ // Single IP address (v4 and v6).
+ t.expectEquals("127.0.0.1", sortIpAddressList("127.0.0.1"));
+ t.expectEquals("::1", sortIpAddressList("::1"))
+
+ // Verify that IPv6 address is not re-written (not reduced).
+ t.expectEquals("0:0::1;192.168.1.1", sortIpAddressList("192.168.1.1;0:0::1"));
+
+ // Input is already sorted.
+ t.expectEquals("::1;192.168.1.3", sortIpAddressList("::1;192.168.1.3"));
+
+ // Same-valued IP addresses (also tests stability).
+ t.expectEquals("0::1;::1;0:0::1", sortIpAddressList("0::1;::1;0:0::1"));
+
+ // Contains extra semi-colons.
+ t.expectEquals("127.0.0.1", sortIpAddressList(";127.0.0.1;"));
+
+ // Contains whitespace (spaces and tabs).
+ t.expectEquals("192.168.0.1;192.168.0.2",
+ sortIpAddressList("192.168.0.1; 192.168.0.2"));
+ t.expectEquals("127.0.0.0;127.0.0.1;127.0.0.2",
+ sortIpAddressList("127.0.0.1; 127.0.0.2; 127.0.0.0"));
+
+ // Empty lists.
+ t.expectFalse(sortIpAddressList(""));
+ t.expectFalse(sortIpAddressList(" "));
+ t.expectFalse(sortIpAddressList(";"));
+ t.expectFalse(sortIpAddressList(";;"));
+ t.expectFalse(sortIpAddressList(" ; ; "));
+
+ // Invalid IP addresses.
+ t.expectFalse(sortIpAddressList("256.0.0.1"));
+ t.expectFalse(sortIpAddressList("192.168.1.1;0:0:0:1;127.0.0.1"));
+
+ // Call sortIpAddressList() with wonky arguments.
+ t.expectEquals(null, sortIpAddressList());
+ t.expectEquals(null, sortIpAddressList(null));
+ t.expectEquals(null, sortIpAddressList(null, null));
+};
+
+Tests.testIsInNetEx = function(t) {
+ t.expectTrue(isInNetEx("198.95.249.79", "198.95.249.79/32"));
+ t.expectTrue(isInNetEx("198.95.115.10", "198.95.0.0/16"));
+ t.expectTrue(isInNetEx("198.95.1.1", "198.95.0.0/16"));
+ t.expectTrue(isInNetEx("198.95.1.1", "198.95.3.3/16"));
+ t.expectTrue(isInNetEx("0:0:0:0:0:0:7f00:1", "0:0:0:0:0:0:7f00:1/32"));
+ t.expectTrue(isInNetEx("3ffe:8311:ffff:abcd:1234:dead:beef:101",
+ "3ffe:8311:ffff::/48"));
+
+ // IPv4 and IPv6 mix.
+ t.expectFalse(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16"));
+ t.expectFalse(isInNetEx("192.168.24.3", "fe80:0:0:0:0:0:c0a8:1803/32"));
+
+ t.expectFalse(isInNetEx("198.95.249.78", "198.95.249.79/32"));
+ t.expectFalse(isInNetEx("198.96.115.10", "198.95.0.0/16"));
+ t.expectFalse(isInNetEx("3fff:8311:ffff:abcd:1234:dead:beef:101",
+ "3ffe:8311:ffff::/48"));
+
+ // Call isInNetEx with wonky arguments.
+ t.expectEquals(null, isInNetEx());
+ t.expectEquals(null, isInNetEx(null));
+ t.expectEquals(null, isInNetEx(null, null));
+ t.expectEquals(null, isInNetEx(null, null, null));
+ t.expectEquals(null, isInNetEx("198.95.249.79"));
+
+ // Invalid IP address.
+ t.expectFalse(isInNetEx("256.0.0.1", "198.95.249.79"));
+ t.expectFalse(isInNetEx("127.0.0.1 ", "127.0.0.1/32")); // Extra space.
+
+ // Invalid prefix.
+ t.expectFalse(isInNetEx("198.95.115.10", "198.95.0.0/34"));
+ t.expectFalse(isInNetEx("127.0.0.1", "127.0.0.1")); // Missing '/' in prefix.
+};
+
+Tests.testWeekdayRange = function(t) {
+ // Test with local time.
+ MockDate.setCurrent("Tue Mar 03 2009");
+ t.expectEquals(true, weekdayRange("MON", "FRI"));
+ t.expectEquals(true, weekdayRange("TUE", "FRI"));
+ t.expectEquals(true, weekdayRange("TUE", "TUE"));
+ t.expectEquals(true, weekdayRange("TUE"));
+ t.expectEquals(false, weekdayRange("WED", "FRI"));
+ t.expectEquals(false, weekdayRange("SUN", "MON"));
+ t.expectEquals(false, weekdayRange("SAT"));
+ t.expectEquals(false, weekdayRange("FRI", "MON"));
+
+ // Test with GMT time.
+ MockDate.setCurrent("Tue Mar 03 2009 GMT");
+ t.expectEquals(true, weekdayRange("MON", "FRI", "GMT"));
+ t.expectEquals(true, weekdayRange("TUE", "FRI", "GMT"));
+ t.expectEquals(true, weekdayRange("TUE", "TUE", "GMT"));
+ t.expectEquals(true, weekdayRange("TUE", "GMT"));
+ t.expectEquals(false, weekdayRange("WED", "FRI", "GMT"));
+ t.expectEquals(false, weekdayRange("SUN", "MON", "GMT"));
+ t.expectEquals(false, weekdayRange("SAT", "GMT"));
+};
+
+Tests.testDateRange = function(t) {
+ // dateRange(day)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(3));
+ t.expectEquals(false, dateRange(1));
+
+ // dateRange(day, "GMT")
+ MockDate.setCurrent("Mar 03 2009 GMT");
+ t.expectEquals(true, dateRange(3, "GMT"));
+ t.expectEquals(false, dateRange(1, "GMT"));
+
+ // dateRange(day1, day2)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(1, 4));
+ t.expectEquals(false, dateRange(4, 20));
+
+ // dateRange(day, month)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(3, "MAR"));
+ MockDate.setCurrent("Mar 03 2014");
+ t.expectEquals(true, dateRange(3, "MAR"));
+ // TODO(eroman):
+ //t.expectEquals(false, dateRange(2, "MAR"));
+ //t.expectEquals(false, dateRange(3, "JAN"));
+
+ // dateRange(day, month, year)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(3, "MAR", 2009));
+ t.expectEquals(false, dateRange(4, "MAR", 2009));
+ t.expectEquals(false, dateRange(3, "FEB", 2009));
+ MockDate.setCurrent("Mar 03 2014");
+ t.expectEquals(false, dateRange(3, "MAR", 2009));
+
+ // dateRange(month1, month2)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange("JAN", "MAR"));
+ t.expectEquals(true, dateRange("MAR", "APR"));
+ t.expectEquals(false, dateRange("MAY", "SEP"));
+
+ // dateRange(day1, month1, day2, month2)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(1, "JAN", 3, "MAR"));
+ t.expectEquals(true, dateRange(3, "MAR", 4, "SEP"));
+ t.expectEquals(false, dateRange(4, "MAR", 4, "SEP"));
+
+ // dateRange(month1, year1, month2, year2)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2009));
+ MockDate.setCurrent("Apr 03 2009");
+ t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2010));
+ t.expectEquals(false, dateRange("FEB", 2009, "MAR", 2009));
+
+ // dateRange(day1, month1, year1, day2, month2, year2)
+ MockDate.setCurrent("Mar 03 2009");
+ t.expectEquals(true, dateRange(1, "JAN", 2009, 3, "MAR", 2009));
+ t.expectEquals(true, dateRange(3, "MAR", 2009, 4, "SEP", 2009));
+ t.expectEquals(true, dateRange(3, "JAN", 2009, 4, "FEB", 2010));
+ t.expectEquals(false, dateRange(4, "MAR", 2009, 4, "SEP", 2009));
+};
+
+Tests.testTimeRange = function(t) {
+ // timeRange(hour)
+ MockDate.setCurrent("Mar 03, 2009 03:34:01");
+ t.expectEquals(true, timeRange(3));
+ t.expectEquals(false, timeRange(2));
+
+ // timeRange(hour1, hour2)
+ MockDate.setCurrent("Mar 03, 2009 03:34:01");
+ t.expectEquals(true, timeRange(2, 3));
+ t.expectEquals(true, timeRange(2, 4));
+ t.expectEquals(true, timeRange(3, 5));
+ t.expectEquals(false, timeRange(1, 2));
+ t.expectEquals(false, timeRange(11, 12));
+
+ // timeRange(hour1, min1, hour2, min2)
+ MockDate.setCurrent("Mar 03, 2009 03:34:01");
+ t.expectEquals(true, timeRange(1, 0, 3, 34));
+ t.expectEquals(true, timeRange(1, 0, 3, 35));
+ t.expectEquals(true, timeRange(3, 34, 5, 0));
+ t.expectEquals(false, timeRange(1, 0, 3, 0));
+ t.expectEquals(false, timeRange(11, 0, 16, 0));
+
+ // timeRange(hour1, min1, sec1, hour2, min2, sec2)
+ MockDate.setCurrent("Mar 03, 2009 03:34:14");
+ t.expectEquals(true, timeRange(1, 0, 0, 3, 34, 14));
+ t.expectEquals(false, timeRange(1, 0, 0, 3, 34, 0));
+ t.expectEquals(true, timeRange(1, 0, 0, 3, 35, 0));
+ t.expectEquals(true, timeRange(3, 34, 0, 5, 0, 0));
+ t.expectEquals(false, timeRange(1, 0, 0, 3, 0, 0));
+ t.expectEquals(false, timeRange(11, 0, 0, 16, 0, 0));
+};
+
+// --------------------------
+// TestContext
+// --------------------------
+
+// |name| is the name of the test being executed, it will be used when logging
+// errors.
+function TestContext(name) {
+ this.numFailures_ = 0;
+ this.name_ = name;
+};
+
+TestContext.prototype.failed = function() {
+ return this.numFailures_ != 0;
+};
+
+TestContext.prototype.expectEquals = function(expectation, actual) {
+ if (!(expectation === actual)) {
+ this.numFailures_++;
+ this.log("FAIL: expected: " + expectation + ", actual: " + actual);
+ }
+};
+
+TestContext.prototype.expectTrue = function(x) {
+ this.expectEquals(true, x);
+};
+
+TestContext.prototype.expectFalse = function(x) {
+ this.expectEquals(false, x);
+};
+
+TestContext.prototype.log = function(x) {
+ // Prefix with the test name that generated the log.
+ try {
+ alert(this.name_ + ": " + x);
+ } catch(e) {
+ // In case alert() is not defined.
+ }
+};
+
+// --------------------------
+// MockDate
+// --------------------------
+
+function MockDate() {
+ this.wrappedDate_ = new MockDate.super_(MockDate.currentDateString_);
+};
+
+// Setup the MockDate so it forwards methods to "this.wrappedDate_" (which is a
+// real Date object). We can't simply chain the prototypes since Date() doesn't
+// allow it.
+MockDate.init = function() {
+ MockDate.super_ = Date;
+
+ function createProxyMethod(methodName) {
+ return function() {
+ return this.wrappedDate_[methodName]
+ .apply(this.wrappedDate_, arguments);
+ }
+ };
+
+ for (i in MockDate.methodNames_) {
+ var methodName = MockDate.methodNames_[i];
+ // Don't define the closure directly in the loop body, since Javascript's
+ // crazy scoping rules mean |methodName| actually bleeds out of the loop!
+ MockDate.prototype[methodName] = createProxyMethod(methodName);
+ }
+
+ // Replace the native Date() with our mock.
+ Date = MockDate;
+};
+
+// Unfortunately Date()'s methods are non-enumerable, therefore list manually.
+MockDate.methodNames_ = [
+ "toString", "toDateString", "toTimeString", "toLocaleString",
+ "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime",
+ "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth",
+ "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours",
+ "getMinutes", "getUTCMinutes", "getSeconds", "getUTCSeconds",
+ "getMilliseconds", "getUTCMilliseconds", "getTimezoneOffset", "setTime",
+ "setMilliseconds", "setUTCMilliseconds", "setSeconds", "setUTCSeconds",
+ "setMinutes", "setUTCMinutes", "setHours", "setUTCHours", "setDate",
+ "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear",
+ "toGMTString", "toUTCString", "getYear", "setYear"
+];
+
+MockDate.setCurrent = function(currentDateString) {
+ MockDate.currentDateString_ = currentDateString;
+}
+
+// Bind the methods to proxy requests to the wrapped Date().
+MockDate.init();
+
diff --git a/test/js-unittest/passthrough.js b/test/js-unittest/passthrough.js
new file mode 100644
index 0000000..832ac66
--- /dev/null
+++ b/test/js-unittest/passthrough.js
@@ -0,0 +1,45 @@
+// Return a single-proxy result, which encodes ALL the arguments that were
+// passed to FindProxyForURL().
+
+function FindProxyForURL(url, host) {
+ if (arguments.length != 2) {
+ throw "Wrong number of arguments passed to FindProxyForURL!";
+ return "FAIL";
+ }
+
+ return "PROXY " + makePseudoHost(url + "." + host);
+}
+
+// Form a string that kind-of resembles a host. We will replace any
+// non-alphanumeric character with a dot, then fix up the oddly placed dots.
+function makePseudoHost(str) {
+ var result = "";
+
+ for (var i = 0; i < str.length; ++i) {
+ var c = str.charAt(i);
+ if (!isValidPseudoHostChar(c)) {
+ c = '.'; // Replace unsupported characters with a dot.
+ }
+
+ // Take care not to place multiple adjacent dots,
+ // a dot at the beginning, or a dot at the end.
+ if (c == '.' &&
+ (result.length == 0 ||
+ i == str.length - 1 ||
+ result.charAt(result.length - 1) == '.')) {
+ continue;
+ }
+ result += c;
+ }
+ return result;
+}
+
+function isValidPseudoHostChar(c) {
+ if (c >= '0' && c <= '9')
+ return true;
+ if (c >= 'a' && c <= 'z')
+ return true;
+ if (c >= 'A' && c <= 'Z')
+ return true;
+ return false;
+}
diff --git a/test/js-unittest/return_empty_string.js b/test/js-unittest/return_empty_string.js
new file mode 100644
index 0000000..3342196
--- /dev/null
+++ b/test/js-unittest/return_empty_string.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return "";
+}
+
diff --git a/test/js-unittest/return_function.js b/test/js-unittest/return_function.js
new file mode 100644
index 0000000..9005553
--- /dev/null
+++ b/test/js-unittest/return_function.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return FindProxyForURL;
+}
+
diff --git a/test/js-unittest/return_integer.js b/test/js-unittest/return_integer.js
new file mode 100644
index 0000000..d86b299
--- /dev/null
+++ b/test/js-unittest/return_integer.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return 0;
+}
+
diff --git a/test/js-unittest/return_null.js b/test/js-unittest/return_null.js
new file mode 100644
index 0000000..6cf90c5
--- /dev/null
+++ b/test/js-unittest/return_null.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return null;
+}
+
diff --git a/test/js-unittest/return_object.js b/test/js-unittest/return_object.js
new file mode 100644
index 0000000..3824f8a
--- /dev/null
+++ b/test/js-unittest/return_object.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return {result: "PROXY foo"};
+}
+
diff --git a/test/js-unittest/return_undefined.js b/test/js-unittest/return_undefined.js
new file mode 100644
index 0000000..0f0aa98
--- /dev/null
+++ b/test/js-unittest/return_undefined.js
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host) {
+ return undefined;
+}
+
diff --git a/test/js-unittest/return_unicode.js b/test/js-unittest/return_unicode.js
new file mode 100644
index 0000000..5ecdd1c
--- /dev/null
+++ b/test/js-unittest/return_unicode.js
@@ -0,0 +1,4 @@
+// U+200B is the codepoint for zero-width-space.
+function FindProxyForURL(url, host) {
+ return "PROXY foo.com\u200B";
+}
diff --git a/test/js-unittest/side_effects.js b/test/js-unittest/side_effects.js
new file mode 100644
index 0000000..39b3b2d
--- /dev/null
+++ b/test/js-unittest/side_effects.js
@@ -0,0 +1,10 @@
+if (!gCounter) {
+ // We write it this way so if the script gets loaded twice,
+ // gCounter remains dirty.
+ var gCounter = 0;
+}
+
+function FindProxyForURL(url, host) {
+ return "PROXY sideffect_" + gCounter++;
+}
+
diff --git a/test/js-unittest/simple.js b/test/js-unittest/simple.js
new file mode 100644
index 0000000..c5dfa6d
--- /dev/null
+++ b/test/js-unittest/simple.js
@@ -0,0 +1,21 @@
+// PAC script which uses isInNet on both IP addresses and hosts, and calls
+// isResolvable().
+
+function FindProxyForURL(url, host) {
+ var my_ip = myIpAddress();
+
+ if (isInNet(my_ip, "172.16.0.0", "255.248.0.0")) {
+ return "PROXY a:80";
+ }
+
+ if (url.substring(0, 6) != "https:" &&
+ isInNet(host, "10.0.0.0", "255.0.0.0")) {
+ return "PROXY b:80";
+ }
+
+ if (dnsDomainIs(host, "foo.bar.baz.com") || !isResolvable(host)) {
+ return "PROXY c:100";
+ }
+
+ return "DIRECT";
+}
diff --git a/test/js-unittest/unhandled_exception.js b/test/js-unittest/unhandled_exception.js
new file mode 100644
index 0000000..9cc2856
--- /dev/null
+++ b/test/js-unittest/unhandled_exception.js
@@ -0,0 +1,5 @@
+function FindProxyForURL(url, host) {
+ // This will throw a runtime exception.
+ return "PROXY x" + undefined_variable;
+}
+
diff --git a/test/jstocstring.pl b/test/jstocstring.pl
new file mode 100755
index 0000000..7b50772
--- /dev/null
+++ b/test/jstocstring.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+print "Reading from $ARGV[0]\nWriting to $ARGV[1]\n";
+open(LS, "ls $ARGV[0]|");
+open(FILE, "> $ARGV[1]");
+print FILE "// This file is auto generated using the following command.\n";
+print FILE "// Do not modify.\n";
+print FILE "// \t./jstocstring.pl $ARGV[0] $ARGV[1]\n";
+print FILE "#ifndef PROXY_TEST_SCRIPT_H_\n";
+print FILE "#define PROXY_TEST_SCRIPT_H_\n\n";
+
+while (<LS>) {
+ chomp();
+ open(FH, "cat $ARGV[0]/$_|");
+ if (s/\.js/_JS/) {
+ $upper = uc();
+ print FILE "#define $upper \\\n";
+ while (<FH>) {
+ s/\"/\\\"/g;
+ chomp();
+ print FILE " \"",$_,"\\n\" \\\n";
+ }
+ }
+ print FILE "\n"
+}
+print FILE "#endif //PROXY_TEST_SCRIPT_H_\n";
+close(FILE);
diff --git a/test/proxy_resolver_v8_unittest.cc b/test/proxy_resolver_v8_unittest.cc
new file mode 100644
index 0000000..cfd38bc
--- /dev/null
+++ b/test/proxy_resolver_v8_unittest.cc
@@ -0,0 +1,547 @@
+// Copyright (c) 2010 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.
+
+#define LOG_TAG "ProxyResolverTest"
+
+#include <utils/Log.h>
+#include "android_runtime/AndroidRuntime.h"
+#include <string.h>
+
+#include "proxy_test_script.h"
+#include "proxy_resolver_v8.h"
+#include "include/gtest/gtest.h"
+
+using namespace android;
+namespace net {
+namespace {
+
+// Javascript bindings for ProxyResolverV8, which returns mock values.
+// Each time one of the bindings is called into, we push the input into a
+// list, for later verification.
+class MockJSBindings : public ProxyResolverJSBindings, public ProxyErrorListener {
+ public:
+ MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0) {}
+
+ virtual bool MyIpAddress(std::string* ip_address) {
+ my_ip_address_count++;
+ *ip_address = my_ip_address_result;
+ return !my_ip_address_result.empty();
+ }
+
+ virtual bool MyIpAddressEx(std::string* ip_address_list) {
+ my_ip_address_ex_count++;
+ *ip_address_list = my_ip_address_ex_result;
+ return !my_ip_address_ex_result.empty();
+ }
+
+ virtual bool DnsResolve(const std::string& host, std::string* ip_address) {
+ dns_resolves.push_back(host);
+ *ip_address = dns_resolve_result;
+ return !dns_resolve_result.empty();
+ }
+
+ virtual bool DnsResolveEx(const std::string& host,
+ std::string* ip_address_list) {
+ dns_resolves_ex.push_back(host);
+ *ip_address_list = dns_resolve_ex_result;
+ return !dns_resolve_ex_result.empty();
+ }
+
+ virtual void AlertMessage(String16 message) {
+ String8 m8(message);
+ std::string mstd(m8.string());
+
+ ALOGD("PAC-alert: %s\n", mstd.c_str()); // Helpful when debugging.
+ alerts.push_back(mstd);
+ }
+
+ virtual void ErrorMessage(const String16 message) {
+ String8 m8(message);
+ std::string mstd(m8.string());
+
+ ALOGD("PAC-error: %s\n", mstd.c_str()); // Helpful when debugging.
+ errors.push_back(mstd);
+ }
+
+ virtual void Shutdown() {}
+
+ // Mock values to return.
+ std::string my_ip_address_result;
+ std::string my_ip_address_ex_result;
+ std::string dns_resolve_result;
+ std::string dns_resolve_ex_result;
+
+ // Inputs we got called with.
+ std::vector<std::string> alerts;
+ std::vector<std::string> errors;
+ std::vector<std::string> dns_resolves;
+ std::vector<std::string> dns_resolves_ex;
+ int my_ip_address_count;
+ int my_ip_address_ex_count;
+};
+
+// This is the same as ProxyResolverV8, but it uses mock bindings in place of
+// the default bindings, and has a helper function to load PAC scripts from
+// disk.
+class ProxyResolverV8WithMockBindings : public ProxyResolverV8 {
+ public:
+ ProxyResolverV8WithMockBindings(MockJSBindings* mock_js_bindings) :
+ ProxyResolverV8(mock_js_bindings, mock_js_bindings), mock_js_bindings_(mock_js_bindings) {
+ }
+
+ MockJSBindings* mock_js_bindings() const {
+ return mock_js_bindings_;
+ }
+
+ private:
+ MockJSBindings* mock_js_bindings_;
+};
+
+// Doesn't really matter what these values are for many of the tests.
+const String16 kQueryUrl("http://www.google.com");
+const String16 kQueryHost("www.google.com");
+String16 kResults;
+
+String16 currentPac;
+#define SCRIPT(x) (currentPac = String16(x))
+
+void addString(std::vector<std::string>* list, std::string str) {
+ if (str.compare(0, 6, "DIRECT") == 0) {
+ list->push_back("DIRECT");
+ } else if (str.compare(0, 6, "PROXY ") == 0) {
+ list->push_back(str.substr(6));
+ } else {
+ ALOGE("Unrecognized proxy string");
+ }
+}
+
+std::vector<std::string> string16ToProxyList(String16 response) {
+ std::vector<std::string> ret;
+ String8 response8(response);
+ std::string rstr(response8.string());
+ if (rstr.find(';') == std::string::npos) {
+ addString(&ret, rstr);
+ return ret;
+ }
+ char str[128];
+ rstr.copy(str, 0, rstr.length());
+ const char* pch = strtok(str, ";");
+
+ while (pch != NULL) {
+ // Skip leading whitespace
+ while ((*pch) == ' ') ++pch;
+ std::string pstring(pch);
+ addString(&ret, pstring);
+
+ pch = strtok(NULL, "; \t");
+ }
+
+ return ret;
+}
+
+std::string StringPrintf(std::string str, int d) {
+ char buf[30];
+ sprintf(buf, str.c_str(), d);
+ return std::string(buf);
+}
+
+TEST(ProxyResolverV8Test, Direct) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(proxies.size(), 1U);
+ EXPECT_EQ("DIRECT",proxies[0]);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+}
+
+TEST(ProxyResolverV8Test, ReturnEmptyString) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(RETURN_EMPTY_STRING_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(proxies.size(), 0U);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+}
+
+TEST(ProxyResolverV8Test, Basic) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(PASSTHROUGH_JS));
+ EXPECT_EQ(OK, result);
+
+ // The "FindProxyForURL" of this PAC script simply concatenates all of the
+ // arguments into a pseudo-host. The purpose of this test is to verify that
+ // the correct arguments are being passed to FindProxyForURL().
+ {
+ String16 queryUrl("http://query.com/path");
+ String16 queryHost("query.com");
+ result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ("http.query.com.path.query.com", proxies[0]);
+ }
+ {
+ String16 queryUrl("ftp://query.com:90/path");
+ String16 queryHost("query.com");
+ int result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ // Note that FindProxyForURL(url, host) does not expect |host| to contain
+ // the port number.
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ("ftp.query.com.90.path.query.com", proxies[0]);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+ }
+
+ // We call this so we'll have code coverage of the function and valgrind will
+ // make sure nothing bad happens.
+ //
+ // NOTE: This is here instead of in its own test so that we'll be calling it
+ // after having done something, in hopes it won't be a no-op.
+ resolver.PurgeMemory();
+}
+
+TEST(ProxyResolverV8Test, BadReturnType) {
+ // These are the files of PAC scripts which each return a non-string
+ // types for FindProxyForURL(). They should all fail with
+ // ERR_PAC_SCRIPT_FAILED.
+ static const String16 files[] = {
+ String16(RETURN_UNDEFINED_JS),
+ String16(RETURN_INTEGER_JS),
+ String16(RETURN_FUNCTION_JS),
+ String16(RETURN_OBJECT_JS),
+ String16(RETURN_NULL_JS)
+ };
+
+ for (size_t i = 0; i < 5; ++i) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(files[i]);
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+ EXPECT_EQ(0U, bindings->alerts.size());
+ ASSERT_EQ(1U, bindings->errors.size());
+ EXPECT_EQ("FindProxyForURL() did not return a string.",
+ bindings->errors[0]);
+ }
+}
+
+// Try using a PAC script which defines no "FindProxyForURL" function.
+TEST(ProxyResolverV8Test, NoEntryPoint) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(NO_ENTRYPOINT_JS));
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(ERR_FAILED, result);
+}
+
+// Try loading a malformed PAC script.
+TEST(ProxyResolverV8Test, ParseError) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(MISSING_CLOSE_BRACE_JS));
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(ERR_FAILED, result);
+
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+ EXPECT_EQ(0U, bindings->alerts.size());
+
+ // We get one error during compilation.
+ ASSERT_EQ(1U, bindings->errors.size());
+
+ EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
+ bindings->errors[0]);
+}
+
+// Run a PAC script several times, which has side-effects.
+TEST(ProxyResolverV8Test, SideEffects) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(SIDE_EFFECTS_JS));
+
+ // The PAC script increments a counter each time we invoke it.
+ for (int i = 0; i < 3; ++i) {
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ(StringPrintf("sideffect_%d", i),
+ proxies[0]);
+ }
+
+ // Reload the script -- the javascript environment should be reset, hence
+ // the counter starts over.
+ result = resolver.SetPacScript(SCRIPT(SIDE_EFFECTS_JS));
+ EXPECT_EQ(OK, result);
+
+ for (int i = 0; i < 3; ++i) {
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ(StringPrintf("sideffect_%d", i),
+ proxies[0]);
+ }
+}
+
+// Execute a PAC script which throws an exception in FindProxyForURL.
+TEST(ProxyResolverV8Test, UnhandledException) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(UNHANDLED_EXCEPTION_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+ EXPECT_EQ(0U, bindings->alerts.size());
+ ASSERT_EQ(1U, bindings->errors.size());
+ EXPECT_EQ("Uncaught ReferenceError: undefined_variable is not defined",
+ bindings->errors[0]);
+}
+
+TEST(ProxyResolverV8Test, ReturnUnicode) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(RETURN_UNICODE_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ // The result from this resolve was unparseable, because it
+ // wasn't ASCII.
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+}
+
+// Test the PAC library functions that we expose in the JS environmnet.
+TEST(ProxyResolverV8Test, JavascriptLibrary) {
+ ALOGE("Javascript start");
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(PAC_LIBRARY_UNITTEST_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ // If the javascript side of this unit-test fails, it will throw a javascript
+ // exception. Otherwise it will return "PROXY success:80".
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ("success:80", proxies[0]);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+}
+
+// Try resolving when SetPacScriptByData() has not been called.
+TEST(ProxyResolverV8Test, NoSetPacScript) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+
+
+ // Resolve should fail, as we are not yet initialized with a script.
+ int result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(ERR_FAILED, result);
+
+ // Initialize it.
+ result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
+ EXPECT_EQ(OK, result);
+
+ // Resolve should now succeed.
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(OK, result);
+
+ // Clear it, by initializing with an empty string.
+ resolver.SetPacScript(SCRIPT());
+
+ // Resolve should fail again now.
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(ERR_FAILED, result);
+
+ // Load a good script once more.
+ result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
+ EXPECT_EQ(OK, result);
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+ EXPECT_EQ(OK, result);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+}
+
+// Test marshalling/un-marshalling of values between C++/V8.
+TEST(ProxyResolverV8Test, V8Bindings) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+ bindings->dns_resolve_result = "127.0.0.1";
+ int result = resolver.SetPacScript(SCRIPT(BINDINGS_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ("DIRECT", proxies[0]);
+
+ EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+
+ // Alert was called 5 times.
+ ASSERT_EQ(5U, bindings->alerts.size());
+ EXPECT_EQ("undefined", bindings->alerts[0]);
+ EXPECT_EQ("null", bindings->alerts[1]);
+ EXPECT_EQ("undefined", bindings->alerts[2]);
+ EXPECT_EQ("[object Object]", bindings->alerts[3]);
+ EXPECT_EQ("exception from calling toString()", bindings->alerts[4]);
+
+ // DnsResolve was called 8 times, however only 2 of those were string
+ // parameters. (so 6 of them failed immediately).
+ ASSERT_EQ(2U, bindings->dns_resolves.size());
+ EXPECT_EQ("", bindings->dns_resolves[0]);
+ EXPECT_EQ("arg1", bindings->dns_resolves[1]);
+
+ // MyIpAddress was called two times.
+ EXPECT_EQ(2, bindings->my_ip_address_count);
+
+ // MyIpAddressEx was called once.
+ EXPECT_EQ(1, bindings->my_ip_address_ex_count);
+
+ // DnsResolveEx was called 2 times.
+ ASSERT_EQ(2U, bindings->dns_resolves_ex.size());
+ EXPECT_EQ("is_resolvable", bindings->dns_resolves_ex[0]);
+ EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]);
+}
+
+// Test calling a binding (myIpAddress()) from the script's global scope.
+// http://crbug.com/40026
+TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+
+ int result = resolver.SetPacScript(SCRIPT(BINDING_FROM_GLOBAL_JS));
+ EXPECT_EQ(OK, result);
+
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+
+ // myIpAddress() got called during initialization of the script.
+ EXPECT_EQ(1, bindings->my_ip_address_count);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_NE("DIRECT", proxies[0]);
+ EXPECT_EQ("127.0.0.1:80", proxies[0]);
+
+ // Check that no other bindings were called.
+ EXPECT_EQ(0U, bindings->errors.size());
+ ASSERT_EQ(0U, bindings->alerts.size());
+ ASSERT_EQ(0U, bindings->dns_resolves.size());
+ EXPECT_EQ(0, bindings->my_ip_address_ex_count);
+ ASSERT_EQ(0U, bindings->dns_resolves_ex.size());
+}
+
+// Try loading a PAC script that ends with a comment and has no terminal
+// newline. This should not cause problems with the PAC utility functions
+// that we add to the script's environment.
+// http://crbug.com/22864
+TEST(ProxyResolverV8Test, EndsWithCommentNoNewline) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(ENDS_WITH_COMMENT_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_NE("DIRECT", proxies[0]);
+ EXPECT_EQ("success:80", proxies[0]);
+}
+
+// Try loading a PAC script that ends with a statement and has no terminal
+// newline. This should not cause problems with the PAC utility functions
+// that we add to the script's environment.
+// http://crbug.com/22864
+TEST(ProxyResolverV8Test, EndsWithStatementNoNewline) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(
+ SCRIPT(ENDS_WITH_STATEMENT_NO_SEMICOLON_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_NE("DIRECT", proxies[0]);
+ EXPECT_EQ("success:3", proxies[0]);
+}
+
+// Test the return values from myIpAddress(), myIpAddressEx(), dnsResolve(),
+// dnsResolveEx(), isResolvable(), isResolvableEx(), when the the binding
+// returns empty string (failure). This simulates the return values from
+// those functions when the underlying DNS resolution fails.
+TEST(ProxyResolverV8Test, DNSResolutionFailure) {
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(SCRIPT(DNS_FAIL_JS));
+ EXPECT_EQ(OK, result);
+
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_NE("DIRECT", proxies[0]);
+ EXPECT_EQ("success:80", proxies[0]);
+}
+
+TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
+ return;
+ ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+ int result = resolver.SetPacScript(String16(INTERNATIONAL_DOMAIN_NAMES_JS));
+ EXPECT_EQ(OK, result);
+
+ // Execute FindProxyForURL().
+ result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+ EXPECT_EQ(OK, result);
+ std::vector<std::string> proxies = string16ToProxyList(kResults);
+ EXPECT_EQ(1U, proxies.size());
+ EXPECT_EQ("DIRECT", proxies[0]);
+
+ // Check that the international domain name was converted to punycode
+ // before passing it onto the bindings layer.
+ MockJSBindings* bindings = resolver.mock_js_bindings();
+
+ ASSERT_EQ(1u, bindings->dns_resolves.size());
+ EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]);
+
+ ASSERT_EQ(1u, bindings->dns_resolves_ex.size());
+ EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]);
+}
+
+} // namespace
+} // namespace net
diff --git a/test/proxy_test_script.h b/test/proxy_test_script.h
new file mode 100644
index 0000000..88af872
--- /dev/null
+++ b/test/proxy_test_script.h
@@ -0,0 +1,660 @@
+// This file is auto generated using the following command.
+// Do not modify.
+// ./jstocstring.pl js-unittest proxy_test_script.h
+#ifndef PROXY_TEST_SCRIPT_H_
+#define PROXY_TEST_SCRIPT_H_
+
+#define BINDING_FROM_GLOBAL_JS \
+ "// Calls a bindings outside of FindProxyForURL(). This causes the code to\n" \
+ "// get exercised during initialization.\n" \
+ "\n" \
+ "var x = myIpAddress();\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"PROXY \" + x + \":80\";\n" \
+ "}\n" \
+
+#define BINDINGS_JS \
+ "// Try calling the browser-side bound functions with varying (invalid)\n" \
+ "// inputs. There is no notion of \"success\" for this test, other than\n" \
+ "// verifying the correct C++ bindings were reached with expected values.\n" \
+ "\n" \
+ "function MyObject() {\n" \
+ " this.x = \"3\";\n" \
+ "}\n" \
+ "\n" \
+ "MyObject.prototype.toString = function() {\n" \
+ " throw \"exception from calling toString()\";\n" \
+ "}\n" \
+ "\n" \
+ "function expectEquals(expectation, actual) {\n" \
+ " if (!(expectation === actual)) {\n" \
+ " throw \"FAIL: expected: \" + expectation + \", actual: \" + actual;\n" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " // Call dnsResolve with some wonky arguments.\n" \
+ " // Those expected to fail (because we have passed a non-string parameter)\n" \
+ " // will return |null|, whereas those that have called through to the C++\n" \
+ " // bindings will return '127.0.0.1'.\n" \
+ " expectEquals(null, dnsResolve());\n" \
+ " expectEquals(null, dnsResolve(null));\n" \
+ " expectEquals(null, dnsResolve(undefined));\n" \
+ " expectEquals('127.0.0.1', dnsResolve(\"\"));\n" \
+ " expectEquals(null, dnsResolve({foo: 'bar'}));\n" \
+ " expectEquals(null, dnsResolve(fn));\n" \
+ " expectEquals(null, dnsResolve(['3']));\n" \
+ " expectEquals('127.0.0.1', dnsResolve(\"arg1\", \"arg2\", \"arg3\", \"arg4\"));\n" \
+ "\n" \
+ " // Call alert with some wonky arguments.\n" \
+ " alert();\n" \
+ " alert(null);\n" \
+ " alert(undefined);\n" \
+ " alert({foo:'bar'});\n" \
+ "\n" \
+ " // This should throw an exception when we toString() the argument\n" \
+ " // to alert in the bindings.\n" \
+ " try {\n" \
+ " alert(new MyObject());\n" \
+ " } catch (e) {\n" \
+ " alert(e);\n" \
+ " }\n" \
+ "\n" \
+ " // Call myIpAddress() with wonky arguments\n" \
+ " myIpAddress(null);\n" \
+ " myIpAddress(null, null);\n" \
+ "\n" \
+ " // Call myIpAddressEx() correctly (no arguments).\n" \
+ " myIpAddressEx();\n" \
+ "\n" \
+ " // Call dnsResolveEx() (note that isResolvableEx() implicity calls it.)\n" \
+ " isResolvableEx(\"is_resolvable\");\n" \
+ " dnsResolveEx(\"foobar\");\n" \
+ "\n" \
+ " return \"DIRECT\";\n" \
+ "}\n" \
+ "\n" \
+ "function fn() {}\n" \
+ "\n" \
+
+#define DIRECT_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"DIRECT\";\n" \
+ "}\n" \
+ "\n" \
+
+#define DNS_FAIL_JS \
+ "// This script should be run in an environment where all DNS resolution are\n" \
+ "// failing. It tests that functions return the expected values.\n" \
+ "//\n" \
+ "// Returns \"PROXY success:80\" on success.\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " try {\n" \
+ " expectEq(\"127.0.0.1\", myIpAddress());\n" \
+ " expectEq(\"\", myIpAddressEx());\n" \
+ "\n" \
+ " expectEq(null, dnsResolve(\"not-found\"));\n" \
+ " expectEq(\"\", dnsResolveEx(\"not-found\"));\n" \
+ "\n" \
+ " expectEq(false, isResolvable(\"not-found\"));\n" \
+ " expectEq(false, isResolvableEx(\"not-found\"));\n" \
+ "\n" \
+ " return \"PROXY success:80\";\n" \
+ " } catch(e) {\n" \
+ " alert(e);\n" \
+ " return \"PROXY failed:80\";\n" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "function expectEq(expected, actual) {\n" \
+ " if (expected != actual)\n" \
+ " throw \"Expected \" + expected + \" but was \" + actual;\n" \
+ "}\n" \
+ "\n" \
+
+#define ENDS_WITH_COMMENT_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"PROXY success:80\";\n" \
+ "}\n" \
+ "\n" \
+ "// We end the script with a comment (and no trailing newline).\n" \
+ "// This used to cause problems, because internally ProxyResolverV8\n" \
+ "// would append some functions to the script; the first line of\n" \
+ "// those extra functions was being considered part of the comment.\n" \
+
+#define ENDS_WITH_STATEMENT_NO_SEMICOLON_JS \
+ "// Ends with a statement, and no terminal newline.\n" \
+ "function FindProxyForURL(url, host) { return \"PROXY success:\" + x; }\n" \
+ "x = 3\n" \
+
+#define INTERNATIONAL_DOMAIN_NAMES_JS \
+ "// Try resolving hostnames containing non-ASCII characters.\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " // This international hostname has a non-ASCII character. It is represented\n" \
+ " // in punycode as 'xn--bcher-kva.ch'\n" \
+ " var idn = 'B\u00fccher.ch';\n" \
+ "\n" \
+ " // We disregard the actual return value -- all we care about is that on\n" \
+ " // the C++ end the bindings were passed the punycode equivalent of this\n" \
+ " // unicode hostname.\n" \
+ " dnsResolve(idn);\n" \
+ " dnsResolveEx(idn);\n" \
+ "\n" \
+ " return \"DIRECT\";\n" \
+ "}\n" \
+ "\n" \
+
+#define MISSING_CLOSE_BRACE_JS \
+ "// This PAC script is invalid, because there is a missing close brace\n" \
+ "// on the function FindProxyForURL().\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"DIRECT\";\n" \
+ "\n" \
+
+#define NO_ENTRYPOINT_JS \
+ "var x = \"This is an invalid PAC script because it lacks a \" +\n" \
+ " \"FindProxyForURL() function\";\n" \
+
+#define PAC_LIBRARY_UNITTEST_JS \
+ "// This should output \"PROXY success:80\" if all the tests pass.\n" \
+ "// Otherwise it will output \"PROXY failure:<num-failures>\".\n" \
+ "//\n" \
+ "// This aims to unit-test the PAC library functions, which are\n" \
+ "// exposed in the PAC's execution environment. (Namely, dnsDomainLevels,\n" \
+ "// timeRange, etc.)\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " var numTestsFailed = 0;\n" \
+ "\n" \
+ " // Run all the tests\n" \
+ " for (var test in Tests) {\n" \
+ " var t = new TestContext(test);\n" \
+ "\n" \
+ " // Run the test.\n" \
+ " Tests[test](t);\n" \
+ "\n" \
+ " if (t.failed()) {\n" \
+ " numTestsFailed++;\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
+ " if (numTestsFailed == 0) {\n" \
+ " return \"PROXY success:80\";\n" \
+ " }\n" \
+ " return \"PROXY failure:\" + numTestsFailed;\n" \
+ "}\n" \
+ "\n" \
+ "// --------------------------\n" \
+ "// Tests\n" \
+ "// --------------------------\n" \
+ "\n" \
+ "var Tests = {};\n" \
+ "\n" \
+ "Tests.testDnsDomainIs = function(t) {\n" \
+ " t.expectTrue(dnsDomainIs(\"google.com\", \".com\"));\n" \
+ " t.expectTrue(dnsDomainIs(\"google.co.uk\", \".co.uk\"));\n" \
+ " t.expectFalse(dnsDomainIs(\"google.com\", \".co.uk\"));\n" \
+ " t.expectFalse(dnsDomainIs(\"www.adobe.com\", \".ad\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testDnsDomainLevels = function(t) {\n" \
+ " t.expectEquals(0, dnsDomainLevels(\"www\"));\n" \
+ " t.expectEquals(2, dnsDomainLevels(\"www.google.com\"));\n" \
+ " t.expectEquals(3, dnsDomainLevels(\"192.168.1.1\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testIsInNet = function(t) {\n" \
+ " t.expectTrue(\n" \
+ " isInNet(\"192.89.132.25\", \"192.89.132.25\", \"255.255.255.255\"));\n" \
+ " t.expectFalse(\n" \
+ " isInNet(\"193.89.132.25\", \"192.89.132.25\", \"255.255.255.255\"));\n" \
+ "\n" \
+ " t.expectTrue(isInNet(\"192.89.132.25\", \"192.89.0.0\", \"255.255.0.0\"));\n" \
+ " t.expectFalse(isInNet(\"193.89.132.25\", \"192.89.0.0\", \"255.255.0.0\"));\n" \
+ "\n" \
+ " t.expectFalse(\n" \
+ " isInNet(\"192.89.132.a\", \"192.89.0.0\", \"255.255.0.0\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testIsPlainHostName = function(t) {\n" \
+ " t.expectTrue(isPlainHostName(\"google\"));\n" \
+ " t.expectFalse(isPlainHostName(\"google.com\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testLocalHostOrDomainIs = function(t) {\n" \
+ " t.expectTrue(localHostOrDomainIs(\"www.google.com\", \"www.google.com\"));\n" \
+ " t.expectTrue(localHostOrDomainIs(\"www\", \"www.google.com\"));\n" \
+ " t.expectFalse(localHostOrDomainIs(\"maps.google.com\", \"www.google.com\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testShExpMatch = function(t) {\n" \
+ " t.expectTrue(shExpMatch(\"foo.jpg\", \"*.jpg\"));\n" \
+ " t.expectTrue(shExpMatch(\"foo5.jpg\", \"*o?.jpg\"));\n" \
+ " t.expectFalse(shExpMatch(\"foo.jpg\", \".jpg\"));\n" \
+ " t.expectFalse(shExpMatch(\"foo.jpg\", \"foo\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testSortIpAddressList = function(t) {\n" \
+ " t.expectEquals(\"::1;::2;::3\", sortIpAddressList(\"::2;::3;::1\"));\n" \
+ " t.expectEquals(\n" \
+ " \"2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22\",\n" \
+ " sortIpAddressList(\"157.59.139.22;\" +\n" \
+ " \"2001:4898:28:3:201:2ff:feea:fc14;\" +\n" \
+ " \"fe80::5efe:157:9d3b:8b16\"));\n" \
+ "\n" \
+ " // Single IP address (v4 and v6).\n" \
+ " t.expectEquals(\"127.0.0.1\", sortIpAddressList(\"127.0.0.1\"));\n" \
+ " t.expectEquals(\"::1\", sortIpAddressList(\"::1\"))\n" \
+ "\n" \
+ " // Verify that IPv6 address is not re-written (not reduced).\n" \
+ " t.expectEquals(\"0:0::1;192.168.1.1\", sortIpAddressList(\"192.168.1.1;0:0::1\"));\n" \
+ "\n" \
+ " // Input is already sorted.\n" \
+ " t.expectEquals(\"::1;192.168.1.3\", sortIpAddressList(\"::1;192.168.1.3\"));\n" \
+ "\n" \
+ " // Same-valued IP addresses (also tests stability).\n" \
+ " t.expectEquals(\"0::1;::1;0:0::1\", sortIpAddressList(\"0::1;::1;0:0::1\"));\n" \
+ "\n" \
+ " // Contains extra semi-colons.\n" \
+ " t.expectEquals(\"127.0.0.1\", sortIpAddressList(\";127.0.0.1;\"));\n" \
+ "\n" \
+ " // Contains whitespace (spaces and tabs).\n" \
+ " t.expectEquals(\"192.168.0.1;192.168.0.2\",\n" \
+ " sortIpAddressList(\"192.168.0.1; 192.168.0.2\"));\n" \
+ " t.expectEquals(\"127.0.0.0;127.0.0.1;127.0.0.2\",\n" \
+ " sortIpAddressList(\"127.0.0.1; 127.0.0.2; 127.0.0.0\"));\n" \
+ "\n" \
+ " // Empty lists.\n" \
+ " t.expectFalse(sortIpAddressList(\"\"));\n" \
+ " t.expectFalse(sortIpAddressList(\" \"));\n" \
+ " t.expectFalse(sortIpAddressList(\";\"));\n" \
+ " t.expectFalse(sortIpAddressList(\";;\"));\n" \
+ " t.expectFalse(sortIpAddressList(\" ; ; \"));\n" \
+ "\n" \
+ " // Invalid IP addresses.\n" \
+ " t.expectFalse(sortIpAddressList(\"256.0.0.1\"));\n" \
+ " t.expectFalse(sortIpAddressList(\"192.168.1.1;0:0:0:1;127.0.0.1\"));\n" \
+ "\n" \
+ " // Call sortIpAddressList() with wonky arguments.\n" \
+ " t.expectEquals(null, sortIpAddressList());\n" \
+ " t.expectEquals(null, sortIpAddressList(null));\n" \
+ " t.expectEquals(null, sortIpAddressList(null, null));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testIsInNetEx = function(t) {\n" \
+ " t.expectTrue(isInNetEx(\"198.95.249.79\", \"198.95.249.79/32\"));\n" \
+ " t.expectTrue(isInNetEx(\"198.95.115.10\", \"198.95.0.0/16\"));\n" \
+ " t.expectTrue(isInNetEx(\"198.95.1.1\", \"198.95.0.0/16\"));\n" \
+ " t.expectTrue(isInNetEx(\"198.95.1.1\", \"198.95.3.3/16\"));\n" \
+ " t.expectTrue(isInNetEx(\"0:0:0:0:0:0:7f00:1\", \"0:0:0:0:0:0:7f00:1/32\"));\n" \
+ " t.expectTrue(isInNetEx(\"3ffe:8311:ffff:abcd:1234:dead:beef:101\",\n" \
+ " \"3ffe:8311:ffff::/48\"));\n" \
+ "\n" \
+ " // IPv4 and IPv6 mix.\n" \
+ " t.expectFalse(isInNetEx(\"127.0.0.1\", \"0:0:0:0:0:0:7f00:1/16\"));\n" \
+ " t.expectFalse(isInNetEx(\"192.168.24.3\", \"fe80:0:0:0:0:0:c0a8:1803/32\"));\n" \
+ "\n" \
+ " t.expectFalse(isInNetEx(\"198.95.249.78\", \"198.95.249.79/32\"));\n" \
+ " t.expectFalse(isInNetEx(\"198.96.115.10\", \"198.95.0.0/16\"));\n" \
+ " t.expectFalse(isInNetEx(\"3fff:8311:ffff:abcd:1234:dead:beef:101\",\n" \
+ " \"3ffe:8311:ffff::/48\"));\n" \
+ "\n" \
+ " // Call isInNetEx with wonky arguments.\n" \
+ " t.expectEquals(null, isInNetEx());\n" \
+ " t.expectEquals(null, isInNetEx(null));\n" \
+ " t.expectEquals(null, isInNetEx(null, null));\n" \
+ " t.expectEquals(null, isInNetEx(null, null, null));\n" \
+ " t.expectEquals(null, isInNetEx(\"198.95.249.79\"));\n" \
+ "\n" \
+ " // Invalid IP address.\n" \
+ " t.expectFalse(isInNetEx(\"256.0.0.1\", \"198.95.249.79\"));\n" \
+ " t.expectFalse(isInNetEx(\"127.0.0.1 \", \"127.0.0.1/32\")); // Extra space.\n" \
+ "\n" \
+ " // Invalid prefix.\n" \
+ " t.expectFalse(isInNetEx(\"198.95.115.10\", \"198.95.0.0/34\"));\n" \
+ " t.expectFalse(isInNetEx(\"127.0.0.1\", \"127.0.0.1\")); // Missing '/' in prefix.\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testWeekdayRange = function(t) {\n" \
+ " // Test with local time.\n" \
+ " MockDate.setCurrent(\"Tue Mar 03 2009\");\n" \
+ " t.expectEquals(true, weekdayRange(\"MON\", \"FRI\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\", \"FRI\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\", \"TUE\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"WED\", \"FRI\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"SUN\", \"MON\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"SAT\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"FRI\", \"MON\"));\n" \
+ "\n" \
+ " // Test with GMT time.\n" \
+ " MockDate.setCurrent(\"Tue Mar 03 2009 GMT\");\n" \
+ " t.expectEquals(true, weekdayRange(\"MON\", \"FRI\", \"GMT\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\", \"FRI\", \"GMT\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\", \"TUE\", \"GMT\"));\n" \
+ " t.expectEquals(true, weekdayRange(\"TUE\", \"GMT\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"WED\", \"FRI\", \"GMT\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"SUN\", \"MON\", \"GMT\"));\n" \
+ " t.expectEquals(false, weekdayRange(\"SAT\", \"GMT\"));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testDateRange = function(t) {\n" \
+ " // dateRange(day)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(3));\n" \
+ " t.expectEquals(false, dateRange(1));\n" \
+ "\n" \
+ " // dateRange(day, \"GMT\")\n" \
+ " MockDate.setCurrent(\"Mar 03 2009 GMT\");\n" \
+ " t.expectEquals(true, dateRange(3, \"GMT\"));\n" \
+ " t.expectEquals(false, dateRange(1, \"GMT\"));\n" \
+ "\n" \
+ " // dateRange(day1, day2)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(1, 4));\n" \
+ " t.expectEquals(false, dateRange(4, 20));\n" \
+ "\n" \
+ " // dateRange(day, month)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(3, \"MAR\"));\n" \
+ " MockDate.setCurrent(\"Mar 03 2014\");\n" \
+ " t.expectEquals(true, dateRange(3, \"MAR\"));\n" \
+ " // TODO(eroman):\n" \
+ " //t.expectEquals(false, dateRange(2, \"MAR\"));\n" \
+ " //t.expectEquals(false, dateRange(3, \"JAN\"));\n" \
+ "\n" \
+ " // dateRange(day, month, year)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(3, \"MAR\", 2009));\n" \
+ " t.expectEquals(false, dateRange(4, \"MAR\", 2009));\n" \
+ " t.expectEquals(false, dateRange(3, \"FEB\", 2009));\n" \
+ " MockDate.setCurrent(\"Mar 03 2014\");\n" \
+ " t.expectEquals(false, dateRange(3, \"MAR\", 2009));\n" \
+ "\n" \
+ " // dateRange(month1, month2)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(\"JAN\", \"MAR\"));\n" \
+ " t.expectEquals(true, dateRange(\"MAR\", \"APR\"));\n" \
+ " t.expectEquals(false, dateRange(\"MAY\", \"SEP\"));\n" \
+ "\n" \
+ " // dateRange(day1, month1, day2, month2)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(1, \"JAN\", 3, \"MAR\"));\n" \
+ " t.expectEquals(true, dateRange(3, \"MAR\", 4, \"SEP\"));\n" \
+ " t.expectEquals(false, dateRange(4, \"MAR\", 4, \"SEP\"));\n" \
+ "\n" \
+ " // dateRange(month1, year1, month2, year2)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(\"FEB\", 2009, \"MAR\", 2009));\n" \
+ " MockDate.setCurrent(\"Apr 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(\"FEB\", 2009, \"MAR\", 2010));\n" \
+ " t.expectEquals(false, dateRange(\"FEB\", 2009, \"MAR\", 2009));\n" \
+ "\n" \
+ " // dateRange(day1, month1, year1, day2, month2, year2)\n" \
+ " MockDate.setCurrent(\"Mar 03 2009\");\n" \
+ " t.expectEquals(true, dateRange(1, \"JAN\", 2009, 3, \"MAR\", 2009));\n" \
+ " t.expectEquals(true, dateRange(3, \"MAR\", 2009, 4, \"SEP\", 2009));\n" \
+ " t.expectEquals(true, dateRange(3, \"JAN\", 2009, 4, \"FEB\", 2010));\n" \
+ " t.expectEquals(false, dateRange(4, \"MAR\", 2009, 4, \"SEP\", 2009));\n" \
+ "};\n" \
+ "\n" \
+ "Tests.testTimeRange = function(t) {\n" \
+ " // timeRange(hour)\n" \
+ " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \
+ " t.expectEquals(true, timeRange(3));\n" \
+ " t.expectEquals(false, timeRange(2));\n" \
+ "\n" \
+ " // timeRange(hour1, hour2)\n" \
+ " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \
+ " t.expectEquals(true, timeRange(2, 3));\n" \
+ " t.expectEquals(true, timeRange(2, 4));\n" \
+ " t.expectEquals(true, timeRange(3, 5));\n" \
+ " t.expectEquals(false, timeRange(1, 2));\n" \
+ " t.expectEquals(false, timeRange(11, 12));\n" \
+ "\n" \
+ " // timeRange(hour1, min1, hour2, min2)\n" \
+ " MockDate.setCurrent(\"Mar 03, 2009 03:34:01\");\n" \
+ " t.expectEquals(true, timeRange(1, 0, 3, 34));\n" \
+ " t.expectEquals(true, timeRange(1, 0, 3, 35));\n" \
+ " t.expectEquals(true, timeRange(3, 34, 5, 0));\n" \
+ " t.expectEquals(false, timeRange(1, 0, 3, 0));\n" \
+ " t.expectEquals(false, timeRange(11, 0, 16, 0));\n" \
+ "\n" \
+ " // timeRange(hour1, min1, sec1, hour2, min2, sec2)\n" \
+ " MockDate.setCurrent(\"Mar 03, 2009 03:34:14\");\n" \
+ " t.expectEquals(true, timeRange(1, 0, 0, 3, 34, 14));\n" \
+ " t.expectEquals(false, timeRange(1, 0, 0, 3, 34, 0));\n" \
+ " t.expectEquals(true, timeRange(1, 0, 0, 3, 35, 0));\n" \
+ " t.expectEquals(true, timeRange(3, 34, 0, 5, 0, 0));\n" \
+ " t.expectEquals(false, timeRange(1, 0, 0, 3, 0, 0));\n" \
+ " t.expectEquals(false, timeRange(11, 0, 0, 16, 0, 0));\n" \
+ "};\n" \
+ "\n" \
+ "// --------------------------\n" \
+ "// TestContext\n" \
+ "// --------------------------\n" \
+ "\n" \
+ "// |name| is the name of the test being executed, it will be used when logging\n" \
+ "// errors.\n" \
+ "function TestContext(name) {\n" \
+ " this.numFailures_ = 0;\n" \
+ " this.name_ = name;\n" \
+ "};\n" \
+ "\n" \
+ "TestContext.prototype.failed = function() {\n" \
+ " return this.numFailures_ != 0;\n" \
+ "};\n" \
+ "\n" \
+ "TestContext.prototype.expectEquals = function(expectation, actual) {\n" \
+ " if (!(expectation === actual)) {\n" \
+ " this.numFailures_++;\n" \
+ " this.log(\"FAIL: expected: \" + expectation + \", actual: \" + actual);\n" \
+ " }\n" \
+ "};\n" \
+ "\n" \
+ "TestContext.prototype.expectTrue = function(x) {\n" \
+ " this.expectEquals(true, x);\n" \
+ "};\n" \
+ "\n" \
+ "TestContext.prototype.expectFalse = function(x) {\n" \
+ " this.expectEquals(false, x);\n" \
+ "};\n" \
+ "\n" \
+ "TestContext.prototype.log = function(x) {\n" \
+ " // Prefix with the test name that generated the log.\n" \
+ " try {\n" \
+ " alert(this.name_ + \": \" + x);\n" \
+ " } catch(e) {\n" \
+ " // In case alert() is not defined.\n" \
+ " }\n" \
+ "};\n" \
+ "\n" \
+ "// --------------------------\n" \
+ "// MockDate\n" \
+ "// --------------------------\n" \
+ "\n" \
+ "function MockDate() {\n" \
+ " this.wrappedDate_ = new MockDate.super_(MockDate.currentDateString_);\n" \
+ "};\n" \
+ "\n" \
+ "// Setup the MockDate so it forwards methods to \"this.wrappedDate_\" (which is a\n" \
+ "// real Date object). We can't simply chain the prototypes since Date() doesn't\n" \
+ "// allow it.\n" \
+ "MockDate.init = function() {\n" \
+ " MockDate.super_ = Date;\n" \
+ "\n" \
+ " function createProxyMethod(methodName) {\n" \
+ " return function() {\n" \
+ " return this.wrappedDate_[methodName]\n" \
+ " .apply(this.wrappedDate_, arguments);\n" \
+ " }\n" \
+ " };\n" \
+ "\n" \
+ " for (i in MockDate.methodNames_) {\n" \
+ " var methodName = MockDate.methodNames_[i];\n" \
+ " // Don't define the closure directly in the loop body, since Javascript's\n" \
+ " // crazy scoping rules mean |methodName| actually bleeds out of the loop!\n" \
+ " MockDate.prototype[methodName] = createProxyMethod(methodName);\n" \
+ " }\n" \
+ "\n" \
+ " // Replace the native Date() with our mock.\n" \
+ " Date = MockDate;\n" \
+ "};\n" \
+ "\n" \
+ "// Unfortunately Date()'s methods are non-enumerable, therefore list manually.\n" \
+ "MockDate.methodNames_ = [\n" \
+ " \"toString\", \"toDateString\", \"toTimeString\", \"toLocaleString\",\n" \
+ " \"toLocaleDateString\", \"toLocaleTimeString\", \"valueOf\", \"getTime\",\n" \
+ " \"getFullYear\", \"getUTCFullYear\", \"getMonth\", \"getUTCMonth\",\n" \
+ " \"getDate\", \"getUTCDate\", \"getDay\", \"getUTCDay\", \"getHours\", \"getUTCHours\",\n" \
+ " \"getMinutes\", \"getUTCMinutes\", \"getSeconds\", \"getUTCSeconds\",\n" \
+ " \"getMilliseconds\", \"getUTCMilliseconds\", \"getTimezoneOffset\", \"setTime\",\n" \
+ " \"setMilliseconds\", \"setUTCMilliseconds\", \"setSeconds\", \"setUTCSeconds\",\n" \
+ " \"setMinutes\", \"setUTCMinutes\", \"setHours\", \"setUTCHours\", \"setDate\",\n" \
+ " \"setUTCDate\", \"setMonth\", \"setUTCMonth\", \"setFullYear\", \"setUTCFullYear\",\n" \
+ " \"toGMTString\", \"toUTCString\", \"getYear\", \"setYear\"\n" \
+ "];\n" \
+ "\n" \
+ "MockDate.setCurrent = function(currentDateString) {\n" \
+ " MockDate.currentDateString_ = currentDateString;\n" \
+ "}\n" \
+ "\n" \
+ "// Bind the methods to proxy requests to the wrapped Date().\n" \
+ "MockDate.init();\n" \
+ "\n" \
+
+#define PASSTHROUGH_JS \
+ "// Return a single-proxy result, which encodes ALL the arguments that were\n" \
+ "// passed to FindProxyForURL().\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " if (arguments.length != 2) {\n" \
+ " throw \"Wrong number of arguments passed to FindProxyForURL!\";\n" \
+ " return \"FAIL\";\n" \
+ " }\n" \
+ "\n" \
+ " return \"PROXY \" + makePseudoHost(url + \".\" + host);\n" \
+ "}\n" \
+ "\n" \
+ "// Form a string that kind-of resembles a host. We will replace any\n" \
+ "// non-alphanumeric character with a dot, then fix up the oddly placed dots.\n" \
+ "function makePseudoHost(str) {\n" \
+ " var result = \"\";\n" \
+ "\n" \
+ " for (var i = 0; i < str.length; ++i) {\n" \
+ " var c = str.charAt(i);\n" \
+ " if (!isValidPseudoHostChar(c)) {\n" \
+ " c = '.'; // Replace unsupported characters with a dot.\n" \
+ " }\n" \
+ "\n" \
+ " // Take care not to place multiple adjacent dots,\n" \
+ " // a dot at the beginning, or a dot at the end.\n" \
+ " if (c == '.' &&\n" \
+ " (result.length == 0 || \n" \
+ " i == str.length - 1 ||\n" \
+ " result.charAt(result.length - 1) == '.')) {\n" \
+ " continue;\n" \
+ " }\n" \
+ " result += c;\n" \
+ " }\n" \
+ " return result;\n" \
+ "}\n" \
+ "\n" \
+ "function isValidPseudoHostChar(c) {\n" \
+ " if (c >= '0' && c <= '9')\n" \
+ " return true;\n" \
+ " if (c >= 'a' && c <= 'z')\n" \
+ " return true;\n" \
+ " if (c >= 'A' && c <= 'Z')\n" \
+ " return true;\n" \
+ " return false;\n" \
+ "}\n" \
+
+#define RETURN_EMPTY_STRING_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"\";\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_FUNCTION_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return FindProxyForURL;\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_INTEGER_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return 0;\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_NULL_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return null;\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_OBJECT_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return {result: \"PROXY foo\"};\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_UNDEFINED_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " return undefined;\n" \
+ "}\n" \
+ "\n" \
+
+#define RETURN_UNICODE_JS \
+ "// U+200B is the codepoint for zero-width-space.\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"PROXY foo.com\u200B\";\n" \
+ "}\n" \
+
+#define SIDE_EFFECTS_JS \
+ "if (!gCounter) {\n" \
+ " // We write it this way so if the script gets loaded twice,\n" \
+ " // gCounter remains dirty.\n" \
+ " var gCounter = 0;\n" \
+ "}\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " return \"PROXY sideffect_\" + gCounter++;\n" \
+ "}\n" \
+ "\n" \
+
+#define SIMPLE_JS \
+ "// PAC script which uses isInNet on both IP addresses and hosts, and calls\n" \
+ "// isResolvable().\n" \
+ "\n" \
+ "function FindProxyForURL(url, host) {\n" \
+ " var my_ip = myIpAddress();\n" \
+ "\n" \
+ " if (isInNet(my_ip, \"172.16.0.0\", \"255.248.0.0\")) {\n" \
+ " return \"PROXY a:80\";\n" \
+ " }\n" \
+ "\n" \
+ " if (url.substring(0, 6) != \"https:\" &&\n" \
+ " isInNet(host, \"10.0.0.0\", \"255.0.0.0\")) {\n" \
+ " return \"PROXY b:80\";\n" \
+ " }\n" \
+ "\n" \
+ " if (dnsDomainIs(host, \"foo.bar.baz.com\") || !isResolvable(host)) {\n" \
+ " return \"PROXY c:100\";\n" \
+ " }\n" \
+ "\n" \
+ " return \"DIRECT\";\n" \
+ "}\n" \
+
+#define UNHANDLED_EXCEPTION_JS \
+ "function FindProxyForURL(url, host) {\n" \
+ " // This will throw a runtime exception.\n" \
+ " return \"PROXY x\" + undefined_variable;\n" \
+ "}\n" \
+ "\n" \
+
+#endif //PROXY_TEST_SCRIPT_H_