proxy_script_decider_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 <vector>
6
7#include "base/bind.h"
8#include "base/memory/weak_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "net/base/net_errors.h"
15#include "net/base/net_log.h"
16#include "net/base/net_log_unittest.h"
17#include "net/base/test_completion_callback.h"
18#include "net/dns/mock_host_resolver.h"
19#include "net/proxy/dhcp_proxy_script_fetcher.h"
20#include "net/proxy/proxy_config.h"
21#include "net/proxy/proxy_resolver.h"
22#include "net/proxy/proxy_script_decider.h"
23#include "net/proxy/proxy_script_fetcher.h"
24#include "net/url_request/url_request_context.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27namespace net {
28namespace {
29
30enum Error {
31  kFailedDownloading = -100,
32  kFailedParsing = ERR_PAC_SCRIPT_FAILED,
33};
34
35class Rules {
36 public:
37  struct Rule {
38    Rule(const GURL& url, int fetch_error, bool is_valid_script)
39        : url(url),
40          fetch_error(fetch_error),
41          is_valid_script(is_valid_script) {
42    }
43
44    base::string16 text() const {
45      if (is_valid_script)
46        return UTF8ToUTF16(url.spec() + "!FindProxyForURL");
47      if (fetch_error == OK)
48        return UTF8ToUTF16(url.spec() + "!invalid-script");
49      return base::string16();
50    }
51
52    GURL url;
53    int fetch_error;
54    bool is_valid_script;
55  };
56
57  Rule AddSuccessRule(const char* url) {
58    Rule rule(GURL(url), OK /*fetch_error*/, true);
59    rules_.push_back(rule);
60    return rule;
61  }
62
63  void AddFailDownloadRule(const char* url) {
64    rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
65        false));
66  }
67
68  void AddFailParsingRule(const char* url) {
69    rules_.push_back(Rule(GURL(url), OK /*fetch_error*/, false));
70  }
71
72  const Rule& GetRuleByUrl(const GURL& url) const {
73    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
74         ++it) {
75      if (it->url == url)
76        return *it;
77    }
78    LOG(FATAL) << "Rule not found for " << url;
79    return rules_[0];
80  }
81
82  const Rule& GetRuleByText(const base::string16& text) const {
83    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
84         ++it) {
85      if (it->text() == text)
86        return *it;
87    }
88    LOG(FATAL) << "Rule not found for " << text;
89    return rules_[0];
90  }
91
92 private:
93  typedef std::vector<Rule> RuleList;
94  RuleList rules_;
95};
96
97class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
98 public:
99  explicit RuleBasedProxyScriptFetcher(const Rules* rules)
100      : rules_(rules), request_context_(NULL) {}
101
102  virtual void SetRequestContext(URLRequestContext* context) {
103    request_context_ = context;
104  }
105
106  // ProxyScriptFetcher implementation.
107  virtual int Fetch(const GURL& url,
108                    base::string16* text,
109                    const CompletionCallback& callback) OVERRIDE {
110    const Rules::Rule& rule = rules_->GetRuleByUrl(url);
111    int rv = rule.fetch_error;
112    EXPECT_NE(ERR_UNEXPECTED, rv);
113    if (rv == OK)
114      *text = rule.text();
115    return rv;
116  }
117
118  virtual void Cancel() OVERRIDE {}
119
120  virtual URLRequestContext* GetRequestContext() const OVERRIDE {
121    return request_context_;
122  }
123
124 private:
125  const Rules* rules_;
126  URLRequestContext* request_context_;
127};
128
129// Succeed using custom PAC script.
130TEST(ProxyScriptDeciderTest, CustomPacSucceeds) {
131  Rules rules;
132  RuleBasedProxyScriptFetcher fetcher(&rules);
133  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
134
135  ProxyConfig config;
136  config.set_pac_url(GURL("http://custom/proxy.pac"));
137
138  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
139
140  TestCompletionCallback callback;
141  CapturingNetLog log;
142  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
143  EXPECT_EQ(OK, decider.Start(
144      config, base::TimeDelta(), true, callback.callback()));
145  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
146
147  // Check the NetLog was filled correctly.
148  CapturingNetLog::CapturedEntryList entries;
149  log.GetEntries(&entries);
150
151  EXPECT_EQ(4u, entries.size());
152  EXPECT_TRUE(LogContainsBeginEvent(
153      entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
154  EXPECT_TRUE(LogContainsBeginEvent(
155      entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
156  EXPECT_TRUE(LogContainsEndEvent(
157      entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
158  EXPECT_TRUE(LogContainsEndEvent(
159      entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
160
161  EXPECT_TRUE(decider.effective_config().has_pac_url());
162  EXPECT_EQ(config.pac_url(), decider.effective_config().pac_url());
163}
164
165// Fail downloading the custom PAC script.
166TEST(ProxyScriptDeciderTest, CustomPacFails1) {
167  Rules rules;
168  RuleBasedProxyScriptFetcher fetcher(&rules);
169  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
170
171  ProxyConfig config;
172  config.set_pac_url(GURL("http://custom/proxy.pac"));
173
174  rules.AddFailDownloadRule("http://custom/proxy.pac");
175
176  TestCompletionCallback callback;
177  CapturingNetLog log;
178  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
179  EXPECT_EQ(kFailedDownloading,
180            decider.Start(config, base::TimeDelta(), true,
181                          callback.callback()));
182  EXPECT_EQ(NULL, decider.script_data());
183
184  // Check the NetLog was filled correctly.
185  CapturingNetLog::CapturedEntryList entries;
186  log.GetEntries(&entries);
187
188  EXPECT_EQ(4u, entries.size());
189  EXPECT_TRUE(LogContainsBeginEvent(
190      entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
191  EXPECT_TRUE(LogContainsBeginEvent(
192      entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
193  EXPECT_TRUE(LogContainsEndEvent(
194      entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
195  EXPECT_TRUE(LogContainsEndEvent(
196      entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
197
198  EXPECT_FALSE(decider.effective_config().has_pac_url());
199}
200
201// Fail parsing the custom PAC script.
202TEST(ProxyScriptDeciderTest, CustomPacFails2) {
203  Rules rules;
204  RuleBasedProxyScriptFetcher fetcher(&rules);
205  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
206
207  ProxyConfig config;
208  config.set_pac_url(GURL("http://custom/proxy.pac"));
209
210  rules.AddFailParsingRule("http://custom/proxy.pac");
211
212  TestCompletionCallback callback;
213  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
214  EXPECT_EQ(kFailedParsing,
215            decider.Start(config, base::TimeDelta(), true,
216                          callback.callback()));
217  EXPECT_EQ(NULL, decider.script_data());
218}
219
220// Fail downloading the custom PAC script, because the fetcher was NULL.
221TEST(ProxyScriptDeciderTest, HasNullProxyScriptFetcher) {
222  Rules rules;
223  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
224
225  ProxyConfig config;
226  config.set_pac_url(GURL("http://custom/proxy.pac"));
227
228  TestCompletionCallback callback;
229  ProxyScriptDecider decider(NULL, &dhcp_fetcher, NULL);
230  EXPECT_EQ(ERR_UNEXPECTED,
231            decider.Start(config, base::TimeDelta(), true,
232                          callback.callback()));
233  EXPECT_EQ(NULL, decider.script_data());
234}
235
236// Succeeds in choosing autodetect (WPAD DNS).
237TEST(ProxyScriptDeciderTest, AutodetectSuccess) {
238  Rules rules;
239  RuleBasedProxyScriptFetcher fetcher(&rules);
240  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
241
242  ProxyConfig config;
243  config.set_auto_detect(true);
244
245  Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
246
247  TestCompletionCallback callback;
248  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
249  EXPECT_EQ(OK, decider.Start(
250      config, base::TimeDelta(), true, callback.callback()));
251  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
252
253  EXPECT_TRUE(decider.effective_config().has_pac_url());
254  EXPECT_EQ(rule.url, decider.effective_config().pac_url());
255}
256
257class ProxyScriptDeciderQuickCheckTest : public ::testing::Test {
258 public:
259  ProxyScriptDeciderQuickCheckTest()
260      : rule_(rules_.AddSuccessRule("http://wpad/wpad.dat")),
261        fetcher_(&rules_) { }
262
263  virtual void SetUp() OVERRIDE {
264    request_context_.set_host_resolver(&resolver_);
265    fetcher_.SetRequestContext(&request_context_);
266    config_.set_auto_detect(true);
267    decider_.reset(new ProxyScriptDecider(&fetcher_, &dhcp_fetcher_, NULL));
268  }
269
270  int StartDecider() {
271    return decider_->Start(config_, base::TimeDelta(), true,
272                            callback_.callback());
273  }
274
275 protected:
276  scoped_ptr<ProxyScriptDecider> decider_;
277  MockHostResolver resolver_;
278  Rules rules_;
279  Rules::Rule rule_;
280  TestCompletionCallback callback_;
281
282 private:
283  URLRequestContext request_context_;
284
285  RuleBasedProxyScriptFetcher fetcher_;
286  DoNothingDhcpProxyScriptFetcher dhcp_fetcher_;
287
288  ProxyConfig config_;
289};
290
291// Fails if a synchronous DNS lookup success for wpad causes QuickCheck to fail.
292TEST_F(ProxyScriptDeciderQuickCheckTest, SyncSuccess) {
293  resolver_.set_synchronous_mode(true);
294  resolver_.rules()->AddRule("wpad", "1.2.3.4");
295
296  EXPECT_EQ(OK, StartDecider());
297  EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
298
299  EXPECT_TRUE(decider_->effective_config().has_pac_url());
300  EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
301}
302
303// Fails if an asynchronous DNS lookup success for wpad causes QuickCheck to
304// fail.
305TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncSuccess) {
306  resolver_.set_ondemand_mode(true);
307  resolver_.rules()->AddRule("wpad", "1.2.3.4");
308
309  EXPECT_EQ(ERR_IO_PENDING, StartDecider());
310  ASSERT_TRUE(resolver_.has_pending_requests());
311  resolver_.ResolveAllPending();
312  callback_.WaitForResult();
313  EXPECT_FALSE(resolver_.has_pending_requests());
314  EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
315  EXPECT_TRUE(decider_->effective_config().has_pac_url());
316  EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
317}
318
319// Fails if an asynchronous DNS lookup failure (i.e. an NXDOMAIN) still causes
320// ProxyScriptDecider to yield a PAC URL.
321TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncFail) {
322  resolver_.set_ondemand_mode(true);
323  resolver_.rules()->AddSimulatedFailure("wpad");
324  EXPECT_EQ(ERR_IO_PENDING, StartDecider());
325  ASSERT_TRUE(resolver_.has_pending_requests());
326  resolver_.ResolveAllPending();
327  callback_.WaitForResult();
328  EXPECT_FALSE(decider_->effective_config().has_pac_url());
329}
330
331// Fails if a DNS lookup timeout either causes ProxyScriptDecider to yield a PAC
332// URL or causes ProxyScriptDecider not to cancel its pending resolution.
333TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncTimeout) {
334  resolver_.set_ondemand_mode(true);
335  EXPECT_EQ(ERR_IO_PENDING, StartDecider());
336  ASSERT_TRUE(resolver_.has_pending_requests());
337  callback_.WaitForResult();
338  EXPECT_FALSE(resolver_.has_pending_requests());
339  EXPECT_FALSE(decider_->effective_config().has_pac_url());
340}
341
342// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
343TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess1) {
344  Rules rules;
345  RuleBasedProxyScriptFetcher fetcher(&rules);
346  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
347
348  ProxyConfig config;
349  config.set_auto_detect(true);
350  config.set_pac_url(GURL("http://custom/proxy.pac"));
351
352  rules.AddFailDownloadRule("http://wpad/wpad.dat");
353  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
354
355  TestCompletionCallback callback;
356  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
357  EXPECT_EQ(OK, decider.Start(
358      config, base::TimeDelta(), true, callback.callback()));
359  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
360
361  EXPECT_TRUE(decider.effective_config().has_pac_url());
362  EXPECT_EQ(rule.url, decider.effective_config().pac_url());
363}
364
365// Fails at WPAD (no DHCP config, DNS PAC fails parsing), but succeeds in
366// choosing the custom PAC.
367TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess2) {
368  Rules rules;
369  RuleBasedProxyScriptFetcher fetcher(&rules);
370  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
371
372  ProxyConfig config;
373  config.set_auto_detect(true);
374  config.set_pac_url(GURL("http://custom/proxy.pac"));
375  config.proxy_rules().ParseFromString("unused-manual-proxy:99");
376
377  rules.AddFailParsingRule("http://wpad/wpad.dat");
378  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
379
380  TestCompletionCallback callback;
381  CapturingNetLog log;
382
383  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
384  EXPECT_EQ(OK, decider.Start(config, base::TimeDelta(),
385                          true, callback.callback()));
386  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
387
388  // Verify that the effective configuration no longer contains auto detect or
389  // any of the manual settings.
390  EXPECT_TRUE(decider.effective_config().Equals(
391      ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
392
393  // Check the NetLog was filled correctly.
394  // (Note that various states are repeated since both WPAD and custom
395  // PAC scripts are tried).
396  CapturingNetLog::CapturedEntryList entries;
397  log.GetEntries(&entries);
398
399  EXPECT_EQ(10u, entries.size());
400  EXPECT_TRUE(LogContainsBeginEvent(
401      entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
402  // This is the DHCP phase, which fails fetching rather than parsing, so
403  // there is no pair of SET_PAC_SCRIPT events.
404  EXPECT_TRUE(LogContainsBeginEvent(
405      entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
406  EXPECT_TRUE(LogContainsEndEvent(
407      entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
408  EXPECT_TRUE(LogContainsEvent(
409      entries, 3,
410      NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
411      NetLog::PHASE_NONE));
412  // This is the DNS phase, which attempts a fetch but fails.
413  EXPECT_TRUE(LogContainsBeginEvent(
414      entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
415  EXPECT_TRUE(LogContainsEndEvent(
416      entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
417  EXPECT_TRUE(LogContainsEvent(
418      entries, 6,
419      NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
420      NetLog::PHASE_NONE));
421  // Finally, the custom PAC URL phase.
422  EXPECT_TRUE(LogContainsBeginEvent(
423      entries, 7, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
424  EXPECT_TRUE(LogContainsEndEvent(
425      entries, 8, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
426  EXPECT_TRUE(LogContainsEndEvent(
427      entries, 9, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
428}
429
430// Fails at WPAD (downloading), and fails at custom PAC (downloading).
431TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails1) {
432  Rules rules;
433  RuleBasedProxyScriptFetcher fetcher(&rules);
434  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
435
436  ProxyConfig config;
437  config.set_auto_detect(true);
438  config.set_pac_url(GURL("http://custom/proxy.pac"));
439
440  rules.AddFailDownloadRule("http://wpad/wpad.dat");
441  rules.AddFailDownloadRule("http://custom/proxy.pac");
442
443  TestCompletionCallback callback;
444  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
445  EXPECT_EQ(kFailedDownloading,
446            decider.Start(config, base::TimeDelta(), true,
447                          callback.callback()));
448  EXPECT_EQ(NULL, decider.script_data());
449}
450
451// Fails at WPAD (downloading), and fails at custom PAC (parsing).
452TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails2) {
453  Rules rules;
454  RuleBasedProxyScriptFetcher fetcher(&rules);
455  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
456
457  ProxyConfig config;
458  config.set_auto_detect(true);
459  config.set_pac_url(GURL("http://custom/proxy.pac"));
460
461  rules.AddFailDownloadRule("http://wpad/wpad.dat");
462  rules.AddFailParsingRule("http://custom/proxy.pac");
463
464  TestCompletionCallback callback;
465  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
466  EXPECT_EQ(kFailedParsing,
467            decider.Start(config, base::TimeDelta(), true,
468                          callback.callback()));
469  EXPECT_EQ(NULL, decider.script_data());
470}
471
472// This is a copy-paste of CustomPacFails1, with the exception that we give it
473// a 1 millisecond delay. This means it will now complete asynchronously.
474// Moreover, we test the NetLog to make sure it logged the pause.
475TEST(ProxyScriptDeciderTest, CustomPacFails1_WithPositiveDelay) {
476  Rules rules;
477  RuleBasedProxyScriptFetcher fetcher(&rules);
478  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
479
480  ProxyConfig config;
481  config.set_pac_url(GURL("http://custom/proxy.pac"));
482
483  rules.AddFailDownloadRule("http://custom/proxy.pac");
484
485  TestCompletionCallback callback;
486  CapturingNetLog log;
487  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
488  EXPECT_EQ(ERR_IO_PENDING,
489            decider.Start(config, base::TimeDelta::FromMilliseconds(1),
490                      true, callback.callback()));
491
492  EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
493  EXPECT_EQ(NULL, decider.script_data());
494
495  // Check the NetLog was filled correctly.
496  CapturingNetLog::CapturedEntryList entries;
497  log.GetEntries(&entries);
498
499  EXPECT_EQ(6u, entries.size());
500  EXPECT_TRUE(LogContainsBeginEvent(
501      entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
502  EXPECT_TRUE(LogContainsBeginEvent(
503      entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
504  EXPECT_TRUE(LogContainsEndEvent(
505      entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
506  EXPECT_TRUE(LogContainsBeginEvent(
507      entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
508  EXPECT_TRUE(LogContainsEndEvent(
509      entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
510  EXPECT_TRUE(LogContainsEndEvent(
511      entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
512}
513
514// This is a copy-paste of CustomPacFails1, with the exception that we give it
515// a -5 second delay instead of a 0 ms delay. This change should have no effect
516// so the rest of the test is unchanged.
517TEST(ProxyScriptDeciderTest, CustomPacFails1_WithNegativeDelay) {
518  Rules rules;
519  RuleBasedProxyScriptFetcher fetcher(&rules);
520  DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
521
522  ProxyConfig config;
523  config.set_pac_url(GURL("http://custom/proxy.pac"));
524
525  rules.AddFailDownloadRule("http://custom/proxy.pac");
526
527  TestCompletionCallback callback;
528  CapturingNetLog log;
529  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
530  EXPECT_EQ(kFailedDownloading,
531            decider.Start(config, base::TimeDelta::FromSeconds(-5),
532                          true, callback.callback()));
533  EXPECT_EQ(NULL, decider.script_data());
534
535  // Check the NetLog was filled correctly.
536  CapturingNetLog::CapturedEntryList entries;
537  log.GetEntries(&entries);
538
539  EXPECT_EQ(4u, entries.size());
540  EXPECT_TRUE(LogContainsBeginEvent(
541      entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
542  EXPECT_TRUE(LogContainsBeginEvent(
543      entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
544  EXPECT_TRUE(LogContainsEndEvent(
545      entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
546  EXPECT_TRUE(LogContainsEndEvent(
547      entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
548}
549
550class SynchronousSuccessDhcpFetcher : public DhcpProxyScriptFetcher {
551 public:
552  explicit SynchronousSuccessDhcpFetcher(const base::string16& expected_text)
553      : gurl_("http://dhcppac/"), expected_text_(expected_text) {
554  }
555
556  virtual int Fetch(base::string16* utf16_text,
557                    const CompletionCallback& callback) OVERRIDE {
558    *utf16_text = expected_text_;
559    return OK;
560  }
561
562  virtual void Cancel() OVERRIDE {
563  }
564
565  virtual const GURL& GetPacURL() const OVERRIDE {
566    return gurl_;
567  }
568
569  const base::string16& expected_text() const {
570    return expected_text_;
571  }
572
573 private:
574  GURL gurl_;
575  base::string16 expected_text_;
576
577  DISALLOW_COPY_AND_ASSIGN(SynchronousSuccessDhcpFetcher);
578};
579
580// All of the tests above that use ProxyScriptDecider have tested
581// failure to fetch a PAC file via DHCP configuration, so we now test
582// success at downloading and parsing, and then success at downloading,
583// failure at parsing.
584
585TEST(ProxyScriptDeciderTest, AutodetectDhcpSuccess) {
586  Rules rules;
587  RuleBasedProxyScriptFetcher fetcher(&rules);
588  SynchronousSuccessDhcpFetcher dhcp_fetcher(
589      WideToUTF16(L"http://bingo/!FindProxyForURL"));
590
591  ProxyConfig config;
592  config.set_auto_detect(true);
593
594  rules.AddSuccessRule("http://bingo/");
595  rules.AddFailDownloadRule("http://wpad/wpad.dat");
596
597  TestCompletionCallback callback;
598  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
599  EXPECT_EQ(OK, decider.Start(
600      config, base::TimeDelta(), true, callback.callback()));
601  EXPECT_EQ(dhcp_fetcher.expected_text(),
602            decider.script_data()->utf16());
603
604  EXPECT_TRUE(decider.effective_config().has_pac_url());
605  EXPECT_EQ(GURL("http://dhcppac/"), decider.effective_config().pac_url());
606}
607
608TEST(ProxyScriptDeciderTest, AutodetectDhcpFailParse) {
609  Rules rules;
610  RuleBasedProxyScriptFetcher fetcher(&rules);
611  SynchronousSuccessDhcpFetcher dhcp_fetcher(
612      WideToUTF16(L"http://bingo/!invalid-script"));
613
614  ProxyConfig config;
615  config.set_auto_detect(true);
616
617  rules.AddFailParsingRule("http://bingo/");
618  rules.AddFailDownloadRule("http://wpad/wpad.dat");
619
620  TestCompletionCallback callback;
621  ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
622  // Since there is fallback to DNS-based WPAD, the final error will be that
623  // it failed downloading, not that it failed parsing.
624  EXPECT_EQ(kFailedDownloading,
625      decider.Start(config, base::TimeDelta(), true, callback.callback()));
626  EXPECT_EQ(NULL, decider.script_data());
627
628  EXPECT_FALSE(decider.effective_config().has_pac_url());
629}
630
631class AsyncFailDhcpFetcher
632    : public DhcpProxyScriptFetcher,
633      public base::SupportsWeakPtr<AsyncFailDhcpFetcher> {
634 public:
635  AsyncFailDhcpFetcher() {}
636  virtual ~AsyncFailDhcpFetcher() {}
637
638  virtual int Fetch(base::string16* utf16_text,
639                    const CompletionCallback& callback) OVERRIDE {
640    callback_ = callback;
641    base::MessageLoop::current()->PostTask(
642        FROM_HERE,
643        base::Bind(&AsyncFailDhcpFetcher::CallbackWithFailure, AsWeakPtr()));
644    return ERR_IO_PENDING;
645  }
646
647  virtual void Cancel() OVERRIDE {
648    callback_.Reset();
649  }
650
651  virtual const GURL& GetPacURL() const OVERRIDE {
652    return dummy_gurl_;
653  }
654
655  void CallbackWithFailure() {
656    if (!callback_.is_null())
657      callback_.Run(ERR_PAC_NOT_IN_DHCP);
658  }
659
660 private:
661  GURL dummy_gurl_;
662  CompletionCallback callback_;
663};
664
665TEST(ProxyScriptDeciderTest, DhcpCancelledByDestructor) {
666  // This regression test would crash before
667  // http://codereview.chromium.org/7044058/
668  // Thus, we don't care much about actual results (hence no EXPECT or ASSERT
669  // macros below), just that it doesn't crash.
670  Rules rules;
671  RuleBasedProxyScriptFetcher fetcher(&rules);
672
673  scoped_ptr<AsyncFailDhcpFetcher> dhcp_fetcher(new AsyncFailDhcpFetcher());
674
675  ProxyConfig config;
676  config.set_auto_detect(true);
677  rules.AddFailDownloadRule("http://wpad/wpad.dat");
678
679  TestCompletionCallback callback;
680
681  // Scope so ProxyScriptDecider gets destroyed early.
682  {
683    ProxyScriptDecider decider(&fetcher, dhcp_fetcher.get(), NULL);
684    decider.Start(config, base::TimeDelta(), true, callback.callback());
685  }
686
687  // Run the message loop to let the DHCP fetch complete and post the results
688  // back. Before the fix linked to above, this would try to invoke on
689  // the callback object provided by ProxyScriptDecider after it was
690  // no longer valid.
691  base::MessageLoop::current()->RunUntilIdle();
692}
693
694}  // namespace
695}  // namespace net
696