diff options
author | Jason Monk <jmonk@google.com> | 2013-08-27 10:18:33 -0400 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2013-08-27 13:27:52 -0400 |
commit | f34af54bcab917eb5943d1918f32d26364f45578 (patch) | |
tree | 52084c98b242010fd4f4d9f180c501849cea2af9 | |
parent | c34a8c90027b55d8f5b5c8e90592fafb72b32c3a (diff) | |
download | chromium-libpac-kitkat-release.tar.gz |
Port chromium PAC unit tests to libpacandroid-sdk-4.4.2_r1.0.1android-sdk-4.4.2_r1android-cts-4.4_r4android-cts-4.4_r1android-4.4w_r1android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.4_r0.7android-4.4.4_r2.0.1android-4.4.4_r2android-4.4.4_r1.0.1android-4.4.4_r1android-4.4.3_r1.1.0.1android-4.4.3_r1.1android-4.4.3_r1.0.1android-4.4.3_r1android-4.4.2_r2.0.1android-4.4.2_r2android-4.4.2_r1.0.1android-4.4.2_r1android-4.4.1_r1.0.1android-4.4.1_r1kitkat-wearkitkat-releasekitkat-mr2.2-releasekitkat-mr2.1-releasekitkat-mr2-releasekitkat-mr1.1-releasekitkat-mr1-releasekitkat-devkitkat-cts-releasekitkat-cts-dev
This converts the gtest unit tests for PAC from chromium to libpac.
Bug: 10504578
Change-Id: If0b93133808c425516a6637c2802cb69dc5e2f43
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_ |