Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / proxy / proxy_resolver_v8_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/compiler_specific.h"
6 #include "base/files/file_util.h"
7 #include "base/path_service.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/net_log_unittest.h"
13 #include "net/proxy/proxy_info.h"
14 #include "net/proxy/proxy_resolver_v8.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "url/gurl.h"
17
18 namespace net {
19 namespace {
20
21 // Javascript bindings for ProxyResolverV8, which returns mock values.
22 // Each time one of the bindings is called into, we push the input into a
23 // list, for later verification.
24 class MockJSBindings : public ProxyResolverV8::JSBindings {
25  public:
26   MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0),
27                      should_terminate(false) {}
28
29   virtual void Alert(const base::string16& message) OVERRIDE {
30     VLOG(1) << "PAC-alert: " << message;  // Helpful when debugging.
31     alerts.push_back(base::UTF16ToUTF8(message));
32   }
33
34   virtual bool ResolveDns(const std::string& host,
35                           ResolveDnsOperation op,
36                           std::string* output,
37                           bool* terminate) OVERRIDE {
38     *terminate = should_terminate;
39
40     if (op == MY_IP_ADDRESS) {
41       my_ip_address_count++;
42       *output = my_ip_address_result;
43       return !my_ip_address_result.empty();
44     }
45
46     if (op == MY_IP_ADDRESS_EX) {
47       my_ip_address_ex_count++;
48       *output = my_ip_address_ex_result;
49       return !my_ip_address_ex_result.empty();
50     }
51
52     if (op == DNS_RESOLVE) {
53       dns_resolves.push_back(host);
54       *output = dns_resolve_result;
55       return !dns_resolve_result.empty();
56     }
57
58     if (op == DNS_RESOLVE_EX) {
59       dns_resolves_ex.push_back(host);
60       *output = dns_resolve_ex_result;
61       return !dns_resolve_ex_result.empty();
62     }
63
64     CHECK(false);
65     return false;
66   }
67
68   virtual void OnError(int line_number,
69                        const base::string16& message) OVERRIDE {
70     // Helpful when debugging.
71     VLOG(1) << "PAC-error: [" << line_number << "] " << message;
72
73     errors.push_back(base::UTF16ToUTF8(message));
74     errors_line_number.push_back(line_number);
75   }
76
77   // Mock values to return.
78   std::string my_ip_address_result;
79   std::string my_ip_address_ex_result;
80   std::string dns_resolve_result;
81   std::string dns_resolve_ex_result;
82
83   // Inputs we got called with.
84   std::vector<std::string> alerts;
85   std::vector<std::string> errors;
86   std::vector<int> errors_line_number;
87   std::vector<std::string> dns_resolves;
88   std::vector<std::string> dns_resolves_ex;
89   int my_ip_address_count;
90   int my_ip_address_ex_count;
91
92   // Whether ResolveDns() should terminate script execution.
93   bool should_terminate;
94 };
95
96 // This is the same as ProxyResolverV8, but it uses mock bindings in place of
97 // the default bindings, and has a helper function to load PAC scripts from
98 // disk.
99 class ProxyResolverV8WithMockBindings : public ProxyResolverV8 {
100  public:
101   ProxyResolverV8WithMockBindings() {
102     set_js_bindings(&mock_js_bindings_);
103   }
104
105   virtual ~ProxyResolverV8WithMockBindings() {
106   }
107
108   MockJSBindings* mock_js_bindings() {
109     return &mock_js_bindings_;
110   }
111
112   // Initialize with the PAC script data at |filename|.
113   int SetPacScriptFromDisk(const char* filename) {
114     base::FilePath path;
115     PathService::Get(base::DIR_SOURCE_ROOT, &path);
116     path = path.AppendASCII("net");
117     path = path.AppendASCII("data");
118     path = path.AppendASCII("proxy_resolver_v8_unittest");
119     path = path.AppendASCII(filename);
120
121     // Try to read the file from disk.
122     std::string file_contents;
123     bool ok = base::ReadFileToString(path, &file_contents);
124
125     // If we can't load the file from disk, something is misconfigured.
126     if (!ok) {
127       LOG(ERROR) << "Failed to read file: " << path.value();
128       return ERR_UNEXPECTED;
129     }
130
131     // Load the PAC script into the ProxyResolver.
132     return SetPacScript(ProxyResolverScriptData::FromUTF8(file_contents),
133                         CompletionCallback());
134   }
135
136  private:
137   MockJSBindings mock_js_bindings_;
138 };
139
140 // Doesn't really matter what these values are for many of the tests.
141 const GURL kQueryUrl("http://www.google.com");
142 const GURL kPacUrl;
143
144 TEST(ProxyResolverV8Test, Direct) {
145   ProxyResolverV8WithMockBindings resolver;
146   int result = resolver.SetPacScriptFromDisk("direct.js");
147   EXPECT_EQ(OK, result);
148
149   ProxyInfo proxy_info;
150   CapturingBoundNetLog log;
151   result = resolver.GetProxyForURL(
152       kQueryUrl, &proxy_info, CompletionCallback(), NULL, log.bound());
153
154   EXPECT_EQ(OK, result);
155   EXPECT_TRUE(proxy_info.is_direct());
156
157   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
158   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
159
160   net::CapturingNetLog::CapturedEntryList entries;
161   log.GetEntries(&entries);
162   // No bindings were called, so no log entries.
163   EXPECT_EQ(0u, entries.size());
164 }
165
166 TEST(ProxyResolverV8Test, ReturnEmptyString) {
167   ProxyResolverV8WithMockBindings resolver;
168   int result = resolver.SetPacScriptFromDisk("return_empty_string.js");
169   EXPECT_EQ(OK, result);
170
171   ProxyInfo proxy_info;
172   result = resolver.GetProxyForURL(
173       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
174
175   EXPECT_EQ(OK, result);
176   EXPECT_TRUE(proxy_info.is_direct());
177
178   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
179   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
180 }
181
182 TEST(ProxyResolverV8Test, Basic) {
183   ProxyResolverV8WithMockBindings resolver;
184   int result = resolver.SetPacScriptFromDisk("passthrough.js");
185   EXPECT_EQ(OK, result);
186
187   // The "FindProxyForURL" of this PAC script simply concatenates all of the
188   // arguments into a pseudo-host. The purpose of this test is to verify that
189   // the correct arguments are being passed to FindProxyForURL().
190   {
191     ProxyInfo proxy_info;
192     result = resolver.GetProxyForURL(GURL("http://query.com/path"), &proxy_info,
193                                      CompletionCallback(), NULL, BoundNetLog());
194     EXPECT_EQ(OK, result);
195     EXPECT_EQ("http.query.com.path.query.com:80",
196               proxy_info.proxy_server().ToURI());
197   }
198   {
199     ProxyInfo proxy_info;
200     int result = resolver.GetProxyForURL(
201         GURL("ftp://query.com:90/path"), &proxy_info, CompletionCallback(),
202         NULL, BoundNetLog());
203     EXPECT_EQ(OK, result);
204     // Note that FindProxyForURL(url, host) does not expect |host| to contain
205     // the port number.
206     EXPECT_EQ("ftp.query.com.90.path.query.com:80",
207               proxy_info.proxy_server().ToURI());
208
209     EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
210     EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
211   }
212 }
213
214 TEST(ProxyResolverV8Test, BadReturnType) {
215   // These are the filenames of PAC scripts which each return a non-string
216   // types for FindProxyForURL(). They should all fail with
217   // ERR_PAC_SCRIPT_FAILED.
218   static const char* const filenames[] = {
219       "return_undefined.js",
220       "return_integer.js",
221       "return_function.js",
222       "return_object.js",
223       // TODO(eroman): Should 'null' be considered equivalent to "DIRECT" ?
224       "return_null.js"
225   };
226
227   for (size_t i = 0; i < arraysize(filenames); ++i) {
228     ProxyResolverV8WithMockBindings resolver;
229     int result = resolver.SetPacScriptFromDisk(filenames[i]);
230     EXPECT_EQ(OK, result);
231
232     ProxyInfo proxy_info;
233     result = resolver.GetProxyForURL(
234         kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
235
236     EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
237
238     MockJSBindings* bindings = resolver.mock_js_bindings();
239     EXPECT_EQ(0U, bindings->alerts.size());
240     ASSERT_EQ(1U, bindings->errors.size());
241     EXPECT_EQ("FindProxyForURL() did not return a string.",
242               bindings->errors[0]);
243     EXPECT_EQ(-1, bindings->errors_line_number[0]);
244   }
245 }
246
247 // Try using a PAC script which defines no "FindProxyForURL" function.
248 TEST(ProxyResolverV8Test, NoEntryPoint) {
249   ProxyResolverV8WithMockBindings resolver;
250   int result = resolver.SetPacScriptFromDisk("no_entrypoint.js");
251   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
252
253   ProxyInfo proxy_info;
254   result = resolver.GetProxyForURL(
255       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
256
257   EXPECT_EQ(ERR_FAILED, result);
258 }
259
260 // Try loading a malformed PAC script.
261 TEST(ProxyResolverV8Test, ParseError) {
262   ProxyResolverV8WithMockBindings resolver;
263   int result = resolver.SetPacScriptFromDisk("missing_close_brace.js");
264   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
265
266   ProxyInfo proxy_info;
267   result = resolver.GetProxyForURL(
268       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
269
270   EXPECT_EQ(ERR_FAILED, result);
271
272   MockJSBindings* bindings = resolver.mock_js_bindings();
273   EXPECT_EQ(0U, bindings->alerts.size());
274
275   // We get one error during compilation.
276   ASSERT_EQ(1U, bindings->errors.size());
277
278   EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
279             bindings->errors[0]);
280   EXPECT_EQ(0, bindings->errors_line_number[0]);
281 }
282
283 // Run a PAC script several times, which has side-effects.
284 TEST(ProxyResolverV8Test, SideEffects) {
285   ProxyResolverV8WithMockBindings resolver;
286   int result = resolver.SetPacScriptFromDisk("side_effects.js");
287
288   // The PAC script increments a counter each time we invoke it.
289   for (int i = 0; i < 3; ++i) {
290     ProxyInfo proxy_info;
291     result = resolver.GetProxyForURL(
292         kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
293     EXPECT_EQ(OK, result);
294     EXPECT_EQ(base::StringPrintf("sideffect_%d:80", i),
295               proxy_info.proxy_server().ToURI());
296   }
297
298   // Reload the script -- the javascript environment should be reset, hence
299   // the counter starts over.
300   result = resolver.SetPacScriptFromDisk("side_effects.js");
301   EXPECT_EQ(OK, result);
302
303   for (int i = 0; i < 3; ++i) {
304     ProxyInfo proxy_info;
305     result = resolver.GetProxyForURL(
306         kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
307     EXPECT_EQ(OK, result);
308     EXPECT_EQ(base::StringPrintf("sideffect_%d:80", i),
309               proxy_info.proxy_server().ToURI());
310   }
311 }
312
313 // Execute a PAC script which throws an exception in FindProxyForURL.
314 TEST(ProxyResolverV8Test, UnhandledException) {
315   ProxyResolverV8WithMockBindings resolver;
316   int result = resolver.SetPacScriptFromDisk("unhandled_exception.js");
317   EXPECT_EQ(OK, result);
318
319   ProxyInfo proxy_info;
320   result = resolver.GetProxyForURL(
321       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
322
323   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
324
325   MockJSBindings* bindings = resolver.mock_js_bindings();
326   EXPECT_EQ(0U, bindings->alerts.size());
327   ASSERT_EQ(1U, bindings->errors.size());
328   EXPECT_EQ("Uncaught ReferenceError: undefined_variable is not defined",
329             bindings->errors[0]);
330   EXPECT_EQ(3, bindings->errors_line_number[0]);
331 }
332
333 TEST(ProxyResolverV8Test, ReturnUnicode) {
334   ProxyResolverV8WithMockBindings resolver;
335   int result = resolver.SetPacScriptFromDisk("return_unicode.js");
336   EXPECT_EQ(OK, result);
337
338   ProxyInfo proxy_info;
339   result = resolver.GetProxyForURL(
340       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
341
342   // The result from this resolve was unparseable, because it
343   // wasn't ASCII.
344   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
345 }
346
347 // Test the PAC library functions that we expose in the JS environment.
348 TEST(ProxyResolverV8Test, JavascriptLibrary) {
349   ProxyResolverV8WithMockBindings resolver;
350   int result = resolver.SetPacScriptFromDisk("pac_library_unittest.js");
351   EXPECT_EQ(OK, result);
352
353   ProxyInfo proxy_info;
354   result = resolver.GetProxyForURL(
355       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
356
357   // If the javascript side of this unit-test fails, it will throw a javascript
358   // exception. Otherwise it will return "PROXY success:80".
359   EXPECT_EQ(OK, result);
360   EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
361
362   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
363   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
364 }
365
366 // Try resolving when SetPacScriptByData() has not been called.
367 TEST(ProxyResolverV8Test, NoSetPacScript) {
368   ProxyResolverV8WithMockBindings resolver;
369
370   ProxyInfo proxy_info;
371
372   // Resolve should fail, as we are not yet initialized with a script.
373   int result = resolver.GetProxyForURL(
374       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
375   EXPECT_EQ(ERR_FAILED, result);
376
377   // Initialize it.
378   result = resolver.SetPacScriptFromDisk("direct.js");
379   EXPECT_EQ(OK, result);
380
381   // Resolve should now succeed.
382   result = resolver.GetProxyForURL(
383       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
384   EXPECT_EQ(OK, result);
385
386   // Clear it, by initializing with an empty string.
387   resolver.SetPacScript(
388       ProxyResolverScriptData::FromUTF16(base::string16()),
389       CompletionCallback());
390
391   // Resolve should fail again now.
392   result = resolver.GetProxyForURL(
393       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
394   EXPECT_EQ(ERR_FAILED, result);
395
396   // Load a good script once more.
397   result = resolver.SetPacScriptFromDisk("direct.js");
398   EXPECT_EQ(OK, result);
399   result = resolver.GetProxyForURL(
400       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
401   EXPECT_EQ(OK, result);
402
403   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
404   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
405 }
406
407 // Test marshalling/un-marshalling of values between C++/V8.
408 TEST(ProxyResolverV8Test, V8Bindings) {
409   ProxyResolverV8WithMockBindings resolver;
410   MockJSBindings* bindings = resolver.mock_js_bindings();
411   bindings->dns_resolve_result = "127.0.0.1";
412   int result = resolver.SetPacScriptFromDisk("bindings.js");
413   EXPECT_EQ(OK, result);
414
415   ProxyInfo proxy_info;
416   result = resolver.GetProxyForURL(
417       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
418
419   EXPECT_EQ(OK, result);
420   EXPECT_TRUE(proxy_info.is_direct());
421
422   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
423
424   // Alert was called 5 times.
425   ASSERT_EQ(5U, bindings->alerts.size());
426   EXPECT_EQ("undefined", bindings->alerts[0]);
427   EXPECT_EQ("null", bindings->alerts[1]);
428   EXPECT_EQ("undefined", bindings->alerts[2]);
429   EXPECT_EQ("[object Object]", bindings->alerts[3]);
430   EXPECT_EQ("exception from calling toString()", bindings->alerts[4]);
431
432   // DnsResolve was called 8 times, however only 2 of those were string
433   // parameters. (so 6 of them failed immediately).
434   ASSERT_EQ(2U, bindings->dns_resolves.size());
435   EXPECT_EQ("", bindings->dns_resolves[0]);
436   EXPECT_EQ("arg1", bindings->dns_resolves[1]);
437
438   // MyIpAddress was called two times.
439   EXPECT_EQ(2, bindings->my_ip_address_count);
440
441   // MyIpAddressEx was called once.
442   EXPECT_EQ(1, bindings->my_ip_address_ex_count);
443
444   // DnsResolveEx was called 2 times.
445   ASSERT_EQ(2U, bindings->dns_resolves_ex.size());
446   EXPECT_EQ("is_resolvable", bindings->dns_resolves_ex[0]);
447   EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]);
448 }
449
450 // Test calling a binding (myIpAddress()) from the script's global scope.
451 // http://crbug.com/40026
452 TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) {
453   ProxyResolverV8WithMockBindings resolver;
454
455   int result = resolver.SetPacScriptFromDisk("binding_from_global.js");
456   EXPECT_EQ(OK, result);
457
458   MockJSBindings* bindings = resolver.mock_js_bindings();
459
460   // myIpAddress() got called during initialization of the script.
461   EXPECT_EQ(1, bindings->my_ip_address_count);
462
463   ProxyInfo proxy_info;
464   result = resolver.GetProxyForURL(
465       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
466
467   EXPECT_EQ(OK, result);
468   EXPECT_FALSE(proxy_info.is_direct());
469   EXPECT_EQ("127.0.0.1:80", proxy_info.proxy_server().ToURI());
470
471   // Check that no other bindings were called.
472   EXPECT_EQ(0U, bindings->errors.size());
473   ASSERT_EQ(0U, bindings->alerts.size());
474   ASSERT_EQ(0U, bindings->dns_resolves.size());
475   EXPECT_EQ(0, bindings->my_ip_address_ex_count);
476   ASSERT_EQ(0U, bindings->dns_resolves_ex.size());
477 }
478
479 // Try loading a PAC script that ends with a comment and has no terminal
480 // newline. This should not cause problems with the PAC utility functions
481 // that we add to the script's environment.
482 // http://crbug.com/22864
483 TEST(ProxyResolverV8Test, EndsWithCommentNoNewline) {
484   ProxyResolverV8WithMockBindings resolver;
485   int result = resolver.SetPacScriptFromDisk("ends_with_comment.js");
486   EXPECT_EQ(OK, result);
487
488   ProxyInfo proxy_info;
489   result = resolver.GetProxyForURL(
490       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
491
492   EXPECT_EQ(OK, result);
493   EXPECT_FALSE(proxy_info.is_direct());
494   EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
495 }
496
497 // Try loading a PAC script that ends with a statement and has no terminal
498 // newline. This should not cause problems with the PAC utility functions
499 // that we add to the script's environment.
500 // http://crbug.com/22864
501 TEST(ProxyResolverV8Test, EndsWithStatementNoNewline) {
502   ProxyResolverV8WithMockBindings resolver;
503   int result = resolver.SetPacScriptFromDisk(
504       "ends_with_statement_no_semicolon.js");
505   EXPECT_EQ(OK, result);
506
507   ProxyInfo proxy_info;
508   result = resolver.GetProxyForURL(
509       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
510
511   EXPECT_EQ(OK, result);
512   EXPECT_FALSE(proxy_info.is_direct());
513   EXPECT_EQ("success:3", proxy_info.proxy_server().ToURI());
514 }
515
516 // Test the return values from myIpAddress(), myIpAddressEx(), dnsResolve(),
517 // dnsResolveEx(), isResolvable(), isResolvableEx(), when the the binding
518 // returns empty string (failure). This simulates the return values from
519 // those functions when the underlying DNS resolution fails.
520 TEST(ProxyResolverV8Test, DNSResolutionFailure) {
521   ProxyResolverV8WithMockBindings resolver;
522   int result = resolver.SetPacScriptFromDisk("dns_fail.js");
523   EXPECT_EQ(OK, result);
524
525   ProxyInfo proxy_info;
526   result = resolver.GetProxyForURL(
527       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
528
529   EXPECT_EQ(OK, result);
530   EXPECT_FALSE(proxy_info.is_direct());
531   EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
532 }
533
534 TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
535   ProxyResolverV8WithMockBindings resolver;
536   int result = resolver.SetPacScriptFromDisk("international_domain_names.js");
537   EXPECT_EQ(OK, result);
538
539   // Execute FindProxyForURL().
540   ProxyInfo proxy_info;
541   result = resolver.GetProxyForURL(
542       kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
543
544   EXPECT_EQ(OK, result);
545   EXPECT_TRUE(proxy_info.is_direct());
546
547   // Check that the international domain name was converted to punycode
548   // before passing it onto the bindings layer.
549   MockJSBindings* bindings = resolver.mock_js_bindings();
550
551   ASSERT_EQ(1u, bindings->dns_resolves.size());
552   EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]);
553
554   ASSERT_EQ(1u, bindings->dns_resolves_ex.size());
555   EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]);
556 }
557
558 // Test that when resolving a URL which contains an IPv6 string literal, the
559 // brackets are removed from the host before passing it down to the PAC script.
560 // If we don't do this, then subsequent calls to dnsResolveEx(host) will be
561 // doomed to fail since it won't correspond with a valid name.
562 TEST(ProxyResolverV8Test, IPv6HostnamesNotBracketed) {
563   ProxyResolverV8WithMockBindings resolver;
564   int result = resolver.SetPacScriptFromDisk("resolve_host.js");
565   EXPECT_EQ(OK, result);
566
567   ProxyInfo proxy_info;
568   result = resolver.GetProxyForURL(
569       GURL("http://[abcd::efff]:99/watsupdawg"), &proxy_info,
570       CompletionCallback(), NULL, BoundNetLog());
571
572   EXPECT_EQ(OK, result);
573   EXPECT_TRUE(proxy_info.is_direct());
574
575   // We called dnsResolveEx() exactly once, by passing through the "host"
576   // argument to FindProxyForURL(). The brackets should have been stripped.
577   ASSERT_EQ(1U, resolver.mock_js_bindings()->dns_resolves_ex.size());
578   EXPECT_EQ("abcd::efff", resolver.mock_js_bindings()->dns_resolves_ex[0]);
579 }
580
581 // Test that terminating a script within DnsResolve() leads to eventual
582 // termination of the script. Also test that repeatedly calling terminate is
583 // safe, and running the script again after termination still works.
584 TEST(ProxyResolverV8Test, Terminate) {
585   ProxyResolverV8WithMockBindings resolver;
586   int result = resolver.SetPacScriptFromDisk("terminate.js");
587   EXPECT_EQ(OK, result);
588
589   MockJSBindings* bindings = resolver.mock_js_bindings();
590
591   // Terminate script execution upon reaching dnsResolve(). Note that
592   // termination may not take effect right away (so the subsequent dnsResolve()
593   // and alert() may be run).
594   bindings->should_terminate = true;
595
596   ProxyInfo proxy_info;
597   result = resolver.GetProxyForURL(
598       GURL("http://hang/"), &proxy_info,
599       CompletionCallback(), NULL, BoundNetLog());
600
601   // The script execution was terminated.
602   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
603
604   EXPECT_EQ(1U, resolver.mock_js_bindings()->dns_resolves.size());
605   EXPECT_GE(2U, resolver.mock_js_bindings()->dns_resolves_ex.size());
606   EXPECT_GE(1U, bindings->alerts.size());
607
608   EXPECT_EQ(1U, bindings->errors.size());
609
610   // Termination shows up as an uncaught exception without any message.
611   EXPECT_EQ("", bindings->errors[0]);
612
613   bindings->errors.clear();
614
615   // Try running the script again, this time with a different input which won't
616   // cause a termination+hang.
617   result = resolver.GetProxyForURL(
618       GURL("http://kittens/"), &proxy_info,
619       CompletionCallback(), NULL, BoundNetLog());
620
621   EXPECT_EQ(OK, result);
622   EXPECT_EQ(0u, bindings->errors.size());
623   EXPECT_EQ("kittens:88", proxy_info.proxy_server().ToURI());
624 }
625
626 }  // namespace
627 }  // namespace net