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.
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"
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 {
26 MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0),
27 should_terminate(false) {}
29 virtual void Alert(const base::string16& message) OVERRIDE {
30 VLOG(1) << "PAC-alert: " << message; // Helpful when debugging.
31 alerts.push_back(UTF16ToUTF8(message));
34 virtual bool ResolveDns(const std::string& host,
35 ResolveDnsOperation op,
37 bool* terminate) OVERRIDE {
38 *terminate = should_terminate;
40 if (op == MY_IP_ADDRESS) {
41 my_ip_address_count++;
42 *output = my_ip_address_result;
43 return !my_ip_address_result.empty();
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();
52 if (op == DNS_RESOLVE) {
53 dns_resolves.push_back(host);
54 *output = dns_resolve_result;
55 return !dns_resolve_result.empty();
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();
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;
73 errors.push_back(UTF16ToUTF8(message));
74 errors_line_number.push_back(line_number);
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;
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;
92 // Whether ResolveDns() should terminate script execution.
93 bool should_terminate;
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
99 class ProxyResolverV8WithMockBindings : public ProxyResolverV8 {
101 ProxyResolverV8WithMockBindings() {
102 set_js_bindings(&mock_js_bindings_);
105 virtual ~ProxyResolverV8WithMockBindings() {
109 MockJSBindings* mock_js_bindings() {
110 return &mock_js_bindings_;
113 // Initialize with the PAC script data at |filename|.
114 int SetPacScriptFromDisk(const char* filename) {
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);
122 // Try to read the file from disk.
123 std::string file_contents;
124 bool ok = base::ReadFileToString(path, &file_contents);
126 // If we can't load the file from disk, something is misconfigured.
128 LOG(ERROR) << "Failed to read file: " << path.value();
129 return ERR_UNEXPECTED;
132 // Load the PAC script into the ProxyResolver.
133 return SetPacScript(ProxyResolverScriptData::FromUTF8(file_contents),
134 CompletionCallback());
138 MockJSBindings mock_js_bindings_;
141 // Doesn't really matter what these values are for many of the tests.
142 const GURL kQueryUrl("http://www.google.com");
145 TEST(ProxyResolverV8Test, Direct) {
146 ProxyResolverV8WithMockBindings resolver;
147 int result = resolver.SetPacScriptFromDisk("direct.js");
148 EXPECT_EQ(OK, result);
150 ProxyInfo proxy_info;
151 CapturingBoundNetLog log;
152 result = resolver.GetProxyForURL(
153 kQueryUrl, &proxy_info, CompletionCallback(), NULL, log.bound());
155 EXPECT_EQ(OK, result);
156 EXPECT_TRUE(proxy_info.is_direct());
158 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
159 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
161 net::CapturingNetLog::CapturedEntryList entries;
162 log.GetEntries(&entries);
163 // No bindings were called, so no log entries.
164 EXPECT_EQ(0u, entries.size());
167 TEST(ProxyResolverV8Test, ReturnEmptyString) {
168 ProxyResolverV8WithMockBindings resolver;
169 int result = resolver.SetPacScriptFromDisk("return_empty_string.js");
170 EXPECT_EQ(OK, result);
172 ProxyInfo proxy_info;
173 result = resolver.GetProxyForURL(
174 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
176 EXPECT_EQ(OK, result);
177 EXPECT_TRUE(proxy_info.is_direct());
179 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
180 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
183 TEST(ProxyResolverV8Test, Basic) {
184 ProxyResolverV8WithMockBindings resolver;
185 int result = resolver.SetPacScriptFromDisk("passthrough.js");
186 EXPECT_EQ(OK, result);
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().
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());
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
207 EXPECT_EQ("ftp.query.com.90.path.query.com:80",
208 proxy_info.proxy_server().ToURI());
210 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
211 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
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",
222 "return_function.js",
224 // TODO(eroman): Should 'null' be considered equivalent to "DIRECT" ?
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);
233 ProxyInfo proxy_info;
234 result = resolver.GetProxyForURL(
235 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
237 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
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]);
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);
254 ProxyInfo proxy_info;
255 result = resolver.GetProxyForURL(
256 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
258 EXPECT_EQ(ERR_FAILED, result);
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);
267 ProxyInfo proxy_info;
268 result = resolver.GetProxyForURL(
269 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
271 EXPECT_EQ(ERR_FAILED, result);
273 MockJSBindings* bindings = resolver.mock_js_bindings();
274 EXPECT_EQ(0U, bindings->alerts.size());
276 // We get one error during compilation.
277 ASSERT_EQ(1U, bindings->errors.size());
279 EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
280 bindings->errors[0]);
281 EXPECT_EQ(0, bindings->errors_line_number[0]);
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");
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());
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);
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());
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);
320 ProxyInfo proxy_info;
321 result = resolver.GetProxyForURL(
322 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
324 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
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]);
334 TEST(ProxyResolverV8Test, ReturnUnicode) {
335 ProxyResolverV8WithMockBindings resolver;
336 int result = resolver.SetPacScriptFromDisk("return_unicode.js");
337 EXPECT_EQ(OK, result);
339 ProxyInfo proxy_info;
340 result = resolver.GetProxyForURL(
341 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
343 // The result from this resolve was unparseable, because it
345 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
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);
354 ProxyInfo proxy_info;
355 result = resolver.GetProxyForURL(
356 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
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());
363 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
364 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
367 // Try resolving when SetPacScriptByData() has not been called.
368 TEST(ProxyResolverV8Test, NoSetPacScript) {
369 ProxyResolverV8WithMockBindings resolver;
371 ProxyInfo proxy_info;
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);
379 result = resolver.SetPacScriptFromDisk("direct.js");
380 EXPECT_EQ(OK, result);
382 // Resolve should now succeed.
383 result = resolver.GetProxyForURL(
384 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
385 EXPECT_EQ(OK, result);
387 // Clear it, by initializing with an empty string.
388 resolver.SetPacScript(
389 ProxyResolverScriptData::FromUTF16(base::string16()),
390 CompletionCallback());
392 // Resolve should fail again now.
393 result = resolver.GetProxyForURL(
394 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
395 EXPECT_EQ(ERR_FAILED, result);
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);
404 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
405 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
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);
416 ProxyInfo proxy_info;
417 result = resolver.GetProxyForURL(
418 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
420 EXPECT_EQ(OK, result);
421 EXPECT_TRUE(proxy_info.is_direct());
423 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
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]);
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]);
439 // MyIpAddress was called two times.
440 EXPECT_EQ(2, bindings->my_ip_address_count);
442 // MyIpAddressEx was called once.
443 EXPECT_EQ(1, bindings->my_ip_address_ex_count);
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]);
451 // Test calling a binding (myIpAddress()) from the script's global scope.
452 // http://crbug.com/40026
453 TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) {
454 ProxyResolverV8WithMockBindings resolver;
456 int result = resolver.SetPacScriptFromDisk("binding_from_global.js");
457 EXPECT_EQ(OK, result);
459 MockJSBindings* bindings = resolver.mock_js_bindings();
461 // myIpAddress() got called during initialization of the script.
462 EXPECT_EQ(1, bindings->my_ip_address_count);
464 ProxyInfo proxy_info;
465 result = resolver.GetProxyForURL(
466 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
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());
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());
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);
489 ProxyInfo proxy_info;
490 result = resolver.GetProxyForURL(
491 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
493 EXPECT_EQ(OK, result);
494 EXPECT_FALSE(proxy_info.is_direct());
495 EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
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);
508 ProxyInfo proxy_info;
509 result = resolver.GetProxyForURL(
510 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
512 EXPECT_EQ(OK, result);
513 EXPECT_FALSE(proxy_info.is_direct());
514 EXPECT_EQ("success:3", proxy_info.proxy_server().ToURI());
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);
526 ProxyInfo proxy_info;
527 result = resolver.GetProxyForURL(
528 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
530 EXPECT_EQ(OK, result);
531 EXPECT_FALSE(proxy_info.is_direct());
532 EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
535 TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
536 ProxyResolverV8WithMockBindings resolver;
537 int result = resolver.SetPacScriptFromDisk("international_domain_names.js");
538 EXPECT_EQ(OK, result);
540 // Execute FindProxyForURL().
541 ProxyInfo proxy_info;
542 result = resolver.GetProxyForURL(
543 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog());
545 EXPECT_EQ(OK, result);
546 EXPECT_TRUE(proxy_info.is_direct());
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();
552 ASSERT_EQ(1u, bindings->dns_resolves.size());
553 EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]);
555 ASSERT_EQ(1u, bindings->dns_resolves_ex.size());
556 EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]);
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);
568 ProxyInfo proxy_info;
569 result = resolver.GetProxyForURL(
570 GURL("http://[abcd::efff]:99/watsupdawg"), &proxy_info,
571 CompletionCallback(), NULL, BoundNetLog());
573 EXPECT_EQ(OK, result);
574 EXPECT_TRUE(proxy_info.is_direct());
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]);
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);
590 MockJSBindings* bindings = resolver.mock_js_bindings();
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;
597 ProxyInfo proxy_info;
598 result = resolver.GetProxyForURL(
599 GURL("http://hang/"), &proxy_info,
600 CompletionCallback(), NULL, BoundNetLog());
602 // The script execution was terminated.
603 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
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());
609 EXPECT_EQ(1U, bindings->errors.size());
611 // Termination shows up as an uncaught exception without any message.
612 EXPECT_EQ("", bindings->errors[0]);
614 bindings->errors.clear();
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());
622 EXPECT_EQ(OK, result);
623 EXPECT_EQ(0u, bindings->errors.size());
624 EXPECT_EQ("kittens:88", proxy_info.proxy_server().ToURI());