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