1// This should output "PROXY success:80" if all the tests pass.
2// Otherwise it will output "PROXY failure:<num-failures>".
3//
4// This aims to unit-test the PAC library functions, which are
5// exposed in the PAC's execution environment. (Namely, dnsDomainLevels,
6// timeRange, etc.)
7
8function FindProxyForURL(url, host) {
9  var numTestsFailed = 0;
10
11  // Run all the tests
12  for (var test in Tests) {
13    var t = new TestContext(test);
14
15    // Run the test.
16    Tests[test](t);
17
18    if (t.failed()) {
19      numTestsFailed++;
20    }
21  }
22
23  if (numTestsFailed == 0) {
24    return "PROXY success:80";
25  }
26  return "PROXY failure:" + numTestsFailed;
27}
28
29// --------------------------
30// Tests
31// --------------------------
32
33var Tests = {};
34
35Tests.testDnsDomainIs = function(t) {
36  t.expectTrue(dnsDomainIs("google.com", ".com"));
37  t.expectTrue(dnsDomainIs("google.co.uk", ".co.uk"));
38  t.expectFalse(dnsDomainIs("google.com", ".co.uk"));
39  t.expectFalse(dnsDomainIs("www.adobe.com", ".ad"));
40};
41
42Tests.testDnsDomainLevels = function(t) {
43  t.expectEquals(0, dnsDomainLevels("www"));
44  t.expectEquals(2, dnsDomainLevels("www.google.com"));
45  t.expectEquals(3, dnsDomainLevels("192.168.1.1"));
46};
47
48Tests.testIsInNet = function(t) {
49  t.expectTrue(
50      isInNet("192.89.132.25", "192.89.132.25", "255.255.255.255"));
51  t.expectFalse(
52      isInNet("193.89.132.25", "192.89.132.25", "255.255.255.255"));
53
54  t.expectTrue(isInNet("192.89.132.25", "192.89.0.0", "255.255.0.0"));
55  t.expectFalse(isInNet("193.89.132.25", "192.89.0.0", "255.255.0.0"));
56
57  t.expectFalse(
58      isInNet("192.89.132.a", "192.89.0.0", "255.255.0.0"));
59};
60
61Tests.testIsPlainHostName = function(t) {
62  t.expectTrue(isPlainHostName("google"));
63  t.expectFalse(isPlainHostName("google.com"));
64};
65
66Tests.testLocalHostOrDomainIs = function(t) {
67  t.expectTrue(localHostOrDomainIs("www.google.com", "www.google.com"));
68  t.expectTrue(localHostOrDomainIs("www", "www.google.com"));
69  t.expectFalse(localHostOrDomainIs("maps.google.com", "www.google.com"));
70};
71
72Tests.testShExpMatch = function(t) {
73  t.expectTrue(shExpMatch("foo.jpg", "*.jpg"));
74  t.expectTrue(shExpMatch("foo5.jpg", "*o?.jpg"));
75  t.expectFalse(shExpMatch("foo.jpg", ".jpg"));
76  t.expectFalse(shExpMatch("foo.jpg", "foo"));
77};
78
79Tests.testSortIpAddressList = function(t) {
80  t.expectEquals("::1;::2;::3", sortIpAddressList("::2;::3;::1"));
81  t.expectEquals(
82      "2001:4898:28:3:201:2ff:feea:fc14;fe80::5efe:157:9d3b:8b16;157.59.139.22",
83      sortIpAddressList("157.59.139.22;" +
84                        "2001:4898:28:3:201:2ff:feea:fc14;" +
85                        "fe80::5efe:157:9d3b:8b16"));
86
87  // Single IP address (v4 and v6).
88  t.expectEquals("127.0.0.1", sortIpAddressList("127.0.0.1"));
89  t.expectEquals("::1", sortIpAddressList("::1"))
90
91  // Verify that IPv6 address is not re-written (not reduced).
92  t.expectEquals("0:0::1;192.168.1.1", sortIpAddressList("192.168.1.1;0:0::1"));
93
94  // Input is already sorted.
95  t.expectEquals("::1;192.168.1.3", sortIpAddressList("::1;192.168.1.3"));
96
97  // Same-valued IP addresses (also tests stability).
98  t.expectEquals("0::1;::1;0:0::1", sortIpAddressList("0::1;::1;0:0::1"));
99
100  // Contains extra semi-colons.
101  t.expectEquals("127.0.0.1", sortIpAddressList(";127.0.0.1;"));
102
103  // Contains whitespace (spaces and tabs).
104  t.expectEquals("192.168.0.1;192.168.0.2",
105      sortIpAddressList("192.168.0.1; 192.168.0.2"));
106  t.expectEquals("127.0.0.0;127.0.0.1;127.0.0.2",
107      sortIpAddressList("127.0.0.1;	127.0.0.2;	 127.0.0.0"));
108
109  // Empty lists.
110  t.expectFalse(sortIpAddressList(""));
111  t.expectFalse(sortIpAddressList(" "));
112  t.expectFalse(sortIpAddressList(";"));
113  t.expectFalse(sortIpAddressList(";;"));
114  t.expectFalse(sortIpAddressList(" ;  ; "));
115
116  // Invalid IP addresses.
117  t.expectFalse(sortIpAddressList("256.0.0.1"));
118  t.expectFalse(sortIpAddressList("192.168.1.1;0:0:0:1;127.0.0.1"));
119
120  // Call sortIpAddressList() with wonky arguments.
121  t.expectEquals(null, sortIpAddressList());
122  t.expectEquals(null, sortIpAddressList(null));
123  t.expectEquals(null, sortIpAddressList(null, null));
124};
125
126Tests.testIsInNetEx = function(t) {
127  t.expectTrue(isInNetEx("198.95.249.79", "198.95.249.79/32"));
128  t.expectTrue(isInNetEx("198.95.115.10", "198.95.0.0/16"));
129  t.expectTrue(isInNetEx("198.95.1.1", "198.95.0.0/16"));
130  t.expectTrue(isInNetEx("198.95.1.1", "198.95.3.3/16"));
131  t.expectTrue(isInNetEx("0:0:0:0:0:0:7f00:1", "0:0:0:0:0:0:7f00:1/32"));
132  t.expectTrue(isInNetEx("3ffe:8311:ffff:abcd:1234:dead:beef:101",
133                         "3ffe:8311:ffff::/48"));
134
135  // IPv4 and IPv6 mix.
136  t.expectFalse(isInNetEx("127.0.0.1", "0:0:0:0:0:0:7f00:1/16"));
137  t.expectFalse(isInNetEx("192.168.24.3", "fe80:0:0:0:0:0:c0a8:1803/32"));
138
139  t.expectFalse(isInNetEx("198.95.249.78", "198.95.249.79/32"));
140  t.expectFalse(isInNetEx("198.96.115.10", "198.95.0.0/16"));
141  t.expectFalse(isInNetEx("3fff:8311:ffff:abcd:1234:dead:beef:101",
142                          "3ffe:8311:ffff::/48"));
143
144  // Call isInNetEx with wonky arguments.
145  t.expectEquals(null, isInNetEx());
146  t.expectEquals(null, isInNetEx(null));
147  t.expectEquals(null, isInNetEx(null, null));
148  t.expectEquals(null, isInNetEx(null, null, null));
149  t.expectEquals(null, isInNetEx("198.95.249.79"));
150
151  // Invalid IP address.
152  t.expectFalse(isInNetEx("256.0.0.1", "198.95.249.79"));
153  t.expectFalse(isInNetEx("127.0.0.1 ", "127.0.0.1/32"));  // Extra space.
154
155  // Invalid prefix.
156  t.expectFalse(isInNetEx("198.95.115.10", "198.95.0.0/34"));
157  t.expectFalse(isInNetEx("127.0.0.1", "127.0.0.1"));  // Missing '/' in prefix.
158};
159
160Tests.testWeekdayRange = function(t) {
161  // Test with local time.
162  MockDate.setCurrent("Tue Mar 03 2009");
163  t.expectEquals(true, weekdayRange("MON", "FRI"));
164  t.expectEquals(true, weekdayRange("TUE", "FRI"));
165  t.expectEquals(true, weekdayRange("TUE", "TUE"));
166  t.expectEquals(true, weekdayRange("TUE"));
167  t.expectEquals(false, weekdayRange("WED", "FRI"));
168  t.expectEquals(false, weekdayRange("SUN", "MON"));
169  t.expectEquals(false, weekdayRange("SAT"));
170  t.expectEquals(false, weekdayRange("FRI", "MON"));
171
172  // Test with GMT time.
173  MockDate.setCurrent("Tue Mar 03 2009 GMT");
174  t.expectEquals(true, weekdayRange("MON", "FRI", "GMT"));
175  t.expectEquals(true, weekdayRange("TUE", "FRI", "GMT"));
176  t.expectEquals(true, weekdayRange("TUE", "TUE", "GMT"));
177  t.expectEquals(true, weekdayRange("TUE", "GMT"));
178  t.expectEquals(false, weekdayRange("WED", "FRI", "GMT"));
179  t.expectEquals(false, weekdayRange("SUN", "MON", "GMT"));
180  t.expectEquals(false, weekdayRange("SAT", "GMT"));
181};
182
183Tests.testDateRange = function(t) {
184  // dateRange(day)
185  MockDate.setCurrent("Mar 03 2009");
186  t.expectEquals(true, dateRange(3));
187  t.expectEquals(false, dateRange(1));
188
189  // dateRange(day, "GMT")
190  MockDate.setCurrent("Mar 03 2009 GMT");
191  t.expectEquals(true, dateRange(3, "GMT"));
192  t.expectEquals(false, dateRange(1, "GMT"));
193
194  // dateRange(day1, day2)
195  MockDate.setCurrent("Mar 03 2009");
196  t.expectEquals(true, dateRange(1, 4));
197  t.expectEquals(false, dateRange(4, 20));
198
199  // dateRange(day, month)
200  MockDate.setCurrent("Mar 03 2009");
201  t.expectEquals(true, dateRange(3, "MAR"));
202  MockDate.setCurrent("Mar 03 2014");
203  t.expectEquals(true, dateRange(3, "MAR"));
204  // TODO(eroman):
205  //t.expectEquals(false, dateRange(2, "MAR"));
206  //t.expectEquals(false, dateRange(3, "JAN"));
207
208  // dateRange(day, month, year)
209  MockDate.setCurrent("Mar 03 2009");
210  t.expectEquals(true, dateRange(3, "MAR", 2009));
211  t.expectEquals(false, dateRange(4, "MAR", 2009));
212  t.expectEquals(false, dateRange(3, "FEB", 2009));
213  MockDate.setCurrent("Mar 03 2014");
214  t.expectEquals(false, dateRange(3, "MAR", 2009));
215
216  // dateRange(month1, month2)
217  MockDate.setCurrent("Mar 03 2009");
218  t.expectEquals(true, dateRange("JAN", "MAR"));
219  t.expectEquals(true, dateRange("MAR", "APR"));
220  t.expectEquals(false, dateRange("MAY", "SEP"));
221
222  // dateRange(day1, month1, day2, month2)
223  MockDate.setCurrent("Mar 03 2009");
224  t.expectEquals(true, dateRange(1, "JAN", 3, "MAR"));
225  t.expectEquals(true, dateRange(3, "MAR", 4, "SEP"));
226  t.expectEquals(false, dateRange(4, "MAR", 4, "SEP"));
227
228  // dateRange(month1, year1, month2, year2)
229  MockDate.setCurrent("Mar 03 2009");
230  t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2009));
231  MockDate.setCurrent("Apr 03 2009");
232  t.expectEquals(true, dateRange("FEB", 2009, "MAR", 2010));
233  t.expectEquals(false, dateRange("FEB", 2009, "MAR", 2009));
234
235  // dateRange(day1, month1, year1, day2, month2, year2)
236  MockDate.setCurrent("Mar 03 2009");
237  t.expectEquals(true, dateRange(1, "JAN", 2009, 3, "MAR", 2009));
238  t.expectEquals(true, dateRange(3, "MAR", 2009, 4, "SEP", 2009));
239  t.expectEquals(true, dateRange(3, "JAN", 2009, 4, "FEB", 2010));
240  t.expectEquals(false, dateRange(4, "MAR", 2009, 4, "SEP", 2009));
241};
242
243Tests.testTimeRange = function(t) {
244  // timeRange(hour)
245  MockDate.setCurrent("Mar 03, 2009 03:34:01");
246  t.expectEquals(true, timeRange(3));
247  t.expectEquals(false, timeRange(2));
248
249  // timeRange(hour1, hour2)
250  MockDate.setCurrent("Mar 03, 2009 03:34:01");
251  t.expectEquals(true, timeRange(2, 3));
252  t.expectEquals(true, timeRange(2, 4));
253  t.expectEquals(true, timeRange(3, 5));
254  t.expectEquals(false, timeRange(1, 2));
255  t.expectEquals(false, timeRange(11, 12));
256
257  // timeRange(hour1, min1, hour2, min2)
258  MockDate.setCurrent("Mar 03, 2009 03:34:01");
259  t.expectEquals(true, timeRange(1, 0, 3, 34));
260  t.expectEquals(true, timeRange(1, 0, 3, 35));
261  t.expectEquals(true, timeRange(3, 34, 5, 0));
262  t.expectEquals(false, timeRange(1, 0, 3, 0));
263  t.expectEquals(false, timeRange(11, 0, 16, 0));
264
265  // timeRange(hour1, min1, sec1, hour2, min2, sec2)
266  MockDate.setCurrent("Mar 03, 2009 03:34:14");
267  t.expectEquals(true, timeRange(1, 0, 0, 3, 34, 14));
268  t.expectEquals(false, timeRange(1, 0, 0, 3, 34, 0));
269  t.expectEquals(true, timeRange(1, 0, 0, 3, 35, 0));
270  t.expectEquals(true, timeRange(3, 34, 0, 5, 0, 0));
271  t.expectEquals(false, timeRange(1, 0, 0, 3, 0, 0));
272  t.expectEquals(false, timeRange(11, 0, 0, 16, 0, 0));
273};
274
275// --------------------------
276// TestContext
277// --------------------------
278
279// |name| is the name of the test being executed, it will be used when logging
280// errors.
281function TestContext(name) {
282  this.numFailures_ = 0;
283  this.name_ = name;
284};
285
286TestContext.prototype.failed = function() {
287  return this.numFailures_ != 0;
288};
289
290TestContext.prototype.expectEquals = function(expectation, actual) {
291  if (!(expectation === actual)) {
292    this.numFailures_++;
293    this.log("FAIL: expected: " + expectation + ", actual: " + actual);
294  }
295};
296
297TestContext.prototype.expectTrue = function(x) {
298  this.expectEquals(true, x);
299};
300
301TestContext.prototype.expectFalse = function(x) {
302  this.expectEquals(false, x);
303};
304
305TestContext.prototype.log = function(x) {
306  // Prefix with the test name that generated the log.
307  try {
308    alert(this.name_ + ": " + x);
309  } catch(e) {
310    // In case alert() is not defined.
311  }
312};
313
314// --------------------------
315// MockDate
316// --------------------------
317
318function MockDate() {
319  this.wrappedDate_ = new MockDate.super_(MockDate.currentDateString_);
320};
321
322// Setup the MockDate so it forwards methods to "this.wrappedDate_" (which is a
323// real Date object).  We can't simply chain the prototypes since Date() doesn't
324// allow it.
325MockDate.init = function() {
326  MockDate.super_ = Date;
327
328  function createProxyMethod(methodName) {
329    return function() {
330      return this.wrappedDate_[methodName]
331          .apply(this.wrappedDate_, arguments);
332    }
333  };
334
335  for (i in MockDate.methodNames_) {
336    var methodName = MockDate.methodNames_[i];
337    // Don't define the closure directly in the loop body, since Javascript's
338    // crazy scoping rules mean |methodName| actually bleeds out of the loop!
339    MockDate.prototype[methodName] = createProxyMethod(methodName);
340  }
341
342  // Replace the native Date() with our mock.
343  Date = MockDate;
344};
345
346// Unfortunately Date()'s methods are non-enumerable, therefore list manually.
347MockDate.methodNames_ = [
348  "toString", "toDateString", "toTimeString", "toLocaleString",
349  "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime",
350  "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth",
351  "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours",
352  "getMinutes", "getUTCMinutes", "getSeconds", "getUTCSeconds",
353  "getMilliseconds", "getUTCMilliseconds", "getTimezoneOffset", "setTime",
354  "setMilliseconds", "setUTCMilliseconds", "setSeconds", "setUTCSeconds",
355  "setMinutes", "setUTCMinutes", "setHours", "setUTCHours", "setDate",
356  "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear",
357  "toGMTString", "toUTCString", "getYear", "setYear"
358];
359
360MockDate.setCurrent = function(currentDateString) {
361  MockDate.currentDateString_ = currentDateString;
362}
363
364// Bind the methods to proxy requests to the wrapped Date().
365MockDate.init();
366
367