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